/*
 * Wazuh Module to analyze system vulnerabilities
 * Copyright (C) 2015-2020, Wazuh Inc.
 * January 4, 2018.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef WIN32

#include "../wmodules.h"
#include "wm_vuln_detector_db.h"
#include "wm_vuln_detector_evr.h"
#include "addagent/manage_agents.h"
#include "wazuh_db/wdb.h"
#include <netinet/tcp.h>
#include <openssl/ssl.h>
#include <os_net/os_net.h>

#ifdef UNIT_TESTING
// Remove STATIC qualifier from tests
#define STATIC
#else
#define STATIC static
#endif

#if defined(__MACH__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#define SOL_TCP     6
#endif

STATIC void *wm_vuldet_main(wm_vuldet_t * vuldet);
STATIC void wm_vuldet_destroy(wm_vuldet_t * vuldet);
STATIC int wm_vuldet_run_update(update_node **updates);
STATIC char *wm_vuldet_oval_xml_preparser(char *path, vu_feed dist);
STATIC int wm_vuldet_index_feed(update_node *update);
STATIC int wm_vuldet_fetch_feed(update_node *update, int *need_update);
STATIC int wm_vuldet_oval_xml_parser(OS_XML *xml, XML_NODE node, wm_vuldet_db *parsed_oval, update_node *update, vu_logic condition);
STATIC int wm_vuldet_json_parser(char *json_path, wm_vuldet_db *uparsed_vulnerabilities, update_node *update);
STATIC void wm_vuldet_add_rvulnerability(wm_vuldet_db *ctrl_block);
STATIC void wm_vuldet_add_vulnerability_info(wm_vuldet_db *ctrl_block);
STATIC references *wm_vuldet_extract_advisories(cJSON *advisories);
STATIC const char *wm_vuldet_decode_package_version(char *raw, const char **OS, char **OS_minor, char **package_name, char **package_version);
STATIC int wm_vuldet_check_db();
STATIC int wm_vuldet_insert(wm_vuldet_db *parsed_oval, update_node *update);
STATIC int wm_vuldet_remove_target_table(sqlite3 *db, char *TABLE, const char *target);

/**
 * @brief Get the Debian status feed to check the status of the vulnerabilities.
 * @param timeout Timeout for the download request
 * @return Parsed JSON on success, NULL otherwise.
 */
STATIC cJSON *wm_vuldet_get_debian_status_feed(const long timeout);

/**
 * @brief Set the status of the packages. Only for Debian OS.
 * @param db Pointer to the CVE DB.
 * @param target Debian OS target.
 * @param timeout Timeout for the download request
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_debian_set_packages_ignore(sqlite3 *db, const char *target, const long timeout);

/**
 * @brief For an specific agent, copy all installed packages' information from SYS_PROGRAM to the AGENTS table.
 * @param agent_software Pointer to an agent node.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_get_software_info(agent_software *agent, sqlite3 *db, time_t ignore_time, wm_vuldet_flags *flags);

/**
 * @brief Search for known vulnerabilities (NVD and OVAL feeds), and report all found CVEs.
 * @param agent_software Pointer to an agent node.
 * @param flags Flag list for Windows agents
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_report_agent_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags);

/**
 * @brief Traverse the agents linked list, gather the installed packages and search
 * for known vulnerabilities.
 * @param agents_software Pointer to the new linked list.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_check_agent_vulnerabilities(agent_software *agents, wm_vuldet_flags *flags, time_t ignore_time);

/**
 * @brief Discard any installed Linux kernel package which is not running.
 * @param agents_software Agent being analyzed.
 * @param name Name of the package.
 * @param version Version of the package.
 * @param arch Architecture of the package.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_discard_kernel_package(agent_software *agent, const char *name, const char *version, const char *arch);

STATIC int wm_vuldet_create_file(const char *path, const char *source);
STATIC int wm_vuldet_check_update_period(update_node *upd);
STATIC int wm_vuldet_update_feed(update_node *upd);
STATIC int wm_vuldet_sync_feed(update_node *upd);
STATIC int wm_vuldet_check_feed(update_node *upd, int *error_code);
STATIC vu_feed wm_vuldet_set_allowed_feed(const char *os_name, const char *os_version, update_node **updates, vu_feed *agent_dist);

/**
 * @brief Initialize the main structure(linked list) of all Syscollector agents
 * for later analysis.
 *
 * @param agents_software Pointer to the new linked list.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_set_agents_info(agent_software **agents_software, update_node **updates);

/**
 * @brief Compare two packages structures (src_name, bin_name, version and arch).
 *
 * @return 0 if equal, -1 otherwise.
 */
STATIC int wm_vuldet_compare_pkg(cve_vuln_pkg *Pkg1, cve_vuln_pkg *Pkg2);

/**
 * @brief Get the next feed related to the given version.
 * @param feed Current feed.
 * @return Next feed.
 */
STATIC vu_feed wm_vuldet_get_next_feed(vu_feed feed);

STATIC cJSON *wm_vuldet_dump(const wm_vuldet_t *vuldet);
STATIC char *wm_vuldet_build_url(char *pattern, char *value);
STATIC void wm_vuldet_adapt_title(char *title, char *cve);
STATIC char *vu_get_version();
STATIC int wm_vuldet_fetch_redhat(update_node *update);
STATIC int wm_vuldet_fetch_oval(update_node *update, char *repo);
STATIC int wm_vuldet_oval_process(update_node *update, char *path, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_json_rh_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities);

/**
 * @brief If a Redhat package (binary) has a source with a different name,
 * save the source name and its version.
 * @param agent Agent being scanned.
 * @param product Original package's name.
 * @param source Original package's source. (input/output)
 * @param version Original package's version.
 * @param src_version Parsed source's version. (output)
 */
STATIC void wm_parse_rpm_source_package(agent_software *agent, const char *product, char **source, const char *version, char **src_version);

STATIC int wm_vuldet_db_empty(sqlite3 *db, vu_feed version);
STATIC int wm_vuldet_nvd_empty();
STATIC const char *wm_vuldet_get_unified_severity(char *severity);
STATIC int wm_vuldet_generate_os_cpe(sqlite3 *db, agent_software *agent, int *min_cpe_index, cpe_list **node_list);
STATIC int wm_vuldet_generate_os_and_kernel_package(sqlite3 *db, agent_software *agent);
STATIC int wm_vuldet_generate_win_cpe(agent_software *agent);
STATIC vu_feed wm_vuldet_decode_win_os(char *os_raw);
STATIC int wm_vuldet_insert_agent_data(sqlite3 *db, agent_software *agent, int *cpe_index, const char *vendor, const char *product, const char *source, const char *version, const char *src_version, const char *arch, cpe *ag_cpe, cpe_list **node_list);
STATIC void wm_vuldet_free_agent_software(agent_software *agent);
STATIC int wm_vuldet_fetch_MSU();
STATIC int wm_vuldet_fetch_wazuh_cpe(update_node *update);
STATIC int wm_vuldet_json_wcpe_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_json_dic_parser(cJSON *json_feed, vu_cpe_dic *wcpes);
STATIC int wm_vuldet_fill_dic_section(cJSON *json_feed, vu_cpe_dic_section **wcpes);
STATIC unsigned int wm_vuldet_fill_action_mask(cJSON *json_feed);
STATIC int wm_vuldet_json_msu_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities);
STATIC int wm_vuldet_insert_cpe_dic(sqlite3 *db, vu_cpe_dic *w_cpes);
STATIC int wm_vuldet_insert_cpe_dic_array(sqlite3 *db, vu_query query, int id, vu_cpe_dic_section *section);
STATIC vu_cpe_dic_node *wm_vuldet_free_dic_node(vu_cpe_dic_node *dic_node);
STATIC int wm_vuldet_insert_MSU(sqlite3 *db, vu_msu_entries *msu);
STATIC vu_msu_vul_entry *wm_vuldet_free_msu_vul_node(vu_msu_vul_entry *node);
STATIC vu_msu_dep_entry *wm_vuldet_free_msu_dep_node(vu_msu_dep_entry *node);
STATIC int wm_vuldet_clean_agent_cpes(char *agent_id);
STATIC int wm_vuldet_get_oswindows_info(agent_software *agent);
STATIC int wm_vuldet_get_oslinux_info(agent_software *agent);
STATIC int wm_vuldet_request_hotfixes(sqlite3 *db, char *agent_id);
STATIC void wm_vuldet_reset_tables(sqlite3 *db);
STATIC int wm_vuldet_index_json(wm_vuldet_db *parsed_vulnerabilities, update_node *update, char *path, char multi_path);
STATIC int wm_vuldet_index_nvd(sqlite3 *db, update_node *upd, nvd_vulnerability *nvd_it);
STATIC int wm_vuldet_index_redhat(sqlite3 *db, update_node *upd, rh_vulnerability *r_it);
STATIC int wm_vuldet_clean_rh(sqlite3 *db);
STATIC int wm_vuldet_clean_wcpe(sqlite3 *db);
STATIC void wm_vuldet_get_package_os(const char *version, const char **os_major, char **os_minor);
STATIC void wm_vuldet_set_subversion(char *version, char **os_minor);
STATIC cJSON *wm_vuldet_json_fread(char *json_file);
STATIC cJSON *wm_vuldet_get_cvss(const char *scoring_vector);
STATIC int wm_vuldet_get_dist_ref(const char *dist_name, const char *dist_ver, int *dist_ref, int *dist_ver_ref);
STATIC int wm_vuldet_get_term_condition(char *i_term, char **term, char **comp_field, char **condition);
STATIC void wm_vuldet_run_scan(wm_vuldet_t *vuldet);
STATIC void wm_vuldet_run_sleep(wm_vuldet_t *vuldet);
STATIC void wm_vuldet_init(wm_vuldet_t *vuldet);
STATIC int wm_vuldet_select_scan_type(char *agent_id, time_t ignore_time, char *hotfix_config_enabled);
STATIC void wm_vuldet_update_last_scan(char *agent_id);
STATIC char *wm_vuldet_get_hotfix_scan(char *agent_id);
STATIC int wm_vuldet_get_last_software_scan(char *agent_id, char scan_id[OS_SIZE_128]);
STATIC char *wm_vuldet_normalize_date(char **date);
STATIC int wm_vulndet_insert_msu_vul_entry(sqlite3 *db, vu_msu_vul_entry *vul);
STATIC void wm_vuldet_json_msu_parser_deps(cJSON *json_feed, vu_msu_dep_entry **msu);
STATIC void wm_vuldet_json_msu_parser_vuln(cJSON *json_feed, vu_msu_vul_entry **msu);
STATIC int wm_vulndet_insert_msu_dep_entry(sqlite3 *db, vu_msu_dep_entry *dep);
STATIC void wm_vuln_check_msu_type(vu_msu_vul_entry *msu, cJSON *patchs);

int wdb_sock = -1;
int *vu_queue;
// Define time to sleep between messages sent
int usec;

const wm_context WM_VULNDETECTOR_CONTEXT = {
    "vulnerability-detector",
    (wm_routine)wm_vuldet_main,
    (wm_routine)(void *)wm_vuldet_destroy,
    (cJSON * (*)(const void *))wm_vuldet_dump
};

const char *version_null = "0:0";

static const char *PR_UBUNTU = "ubuntu_linux";
static const char *PR_DEBIAN = "debian_linux";
static const char *PR_REDHAT = "enterprise_linux";
static const char *PR_KERNEL = "linux_kernel";
static const char *V_UBUNTU = "canonical";
static const char *V_DEBIAN = "debian";
static const char *V_REDHAT = "redhat";
static const char *V_KERNEL = "linux";

const char *vu_package_dist[] = {
    ".el",
    ".el8",
    ".el7",
    ".el6",
    ".el5",
    // Some weird cases
    ".ael8",
    ".ael7",
    ".ael6",
    ".ael5"
};

const char *vu_feed_tag[] = {
    "UBUNTU",
    "CANONICAL",
    "DEBIAN",
    "REDHAT",
    "CENTOS",
    "WIN",
    // Ubuntu versions
    "PRECISE",
    "TRUSTY",
    "XENIAL",
    "BIONIC",
    "FOCAL",
    // Debian versions
    "JESSIE",
    "STRETCH",
    "BUSTER",
    "WHEEZY",
    // RedHat versions
    "RHEL5",
    "RHEL6",
    "RHEL7",
    "RHEL8",
    // NVD
    "NVD",
    "CPED",
    // WINDOWS
    "WS2003",
    "WS2003R2",
    "WXP",
    "WVISTA",
    "W7",
    "W8",
    "W81",
    "W10",
    "WS2008",
    "WS2008R2",
    "WS2012",
    "WS2012R2",
    "WS2016",
    "WS2019",
    // OTHER
    "CPEW",
    "MICROSOFT",
    "UNKNOWN"
};

const char *vu_feed_ext[] = {
    "Ubuntu",
    "Canonical",
    "Debian",
    "Red Hat Enterprise Linux",
    "CentOS",
    "Microsoft Windows",
    // Ubuntu versions
    "Ubuntu Precise",
    "Ubuntu Trusty",
    "Ubuntu Xenial",
    "Ubuntu Bionic",
    "Ubuntu Focal",
    // Debian versions
    "Debian Jessie",
    "Debian Stretch",
    "Debian Buster",
    "Debian Wheezy",
    // RedHat versions
    "Red Hat Enterprise Linux 5",
    "Red Hat Enterprise Linux 6",
    "Red Hat Enterprise Linux 7",
    "Red Hat Enterprise Linux 8",
    // NVD
    "National Vulnerability Database",
    "Common Platform Enumeration Dictionary",
    // WINDOWS
    "Windows Server 2003",
    "Windows Server 2003 R2",
    "Windows XP",
    "Windows Vista",
    "Windows 7",
    "Windows 8",
    "Windows 8.1",
    "Windows 10",
    "Windows Server 2008",
    "Windows Server 2008 R2",
    "Windows Server 2012",
    "Windows Server 2012 R2",
    "Windows Server 2016",
    "Windows Server 2019",
    // OTHER
    "Wazuh CPE dictionary",
    "Microsoft Security Update",
    "Unknown OS"
};

const char *vu_package_comp[] = {
    "less than",
    "less than or equal",
    "greater than",
    "greater than or equal",
    "equal",
    "not equal",
    "exists"
};

const char *vu_severities[] = {
    "Low",
    "Medium",
    "Moderate",
    "Unknown",
    "High",
    "Important",
    "Critical",
    "None",
    "Negligible",
    "Untriaged",
    "-"
};

const char *vu_cpe_dic_option[] = {
    "replace_vendor",
    "replace_product",
    "replace_vendor_if_matches",
    "replace_product_if_matches",
    "set_version_if_matches",
    "replace_sw_edition_if_product_matches",
    "replace_msu_name_if_version_matches",
    "ignore",
    "check_hotfix",
    "replace_msu_name",
    "set_version_if_product_matches",
    "set_targethw_if_product_matches",
    "set_version_only_if_product_matches",
    "set_targethw_only_if_product_matches"
};

vu_feed wm_vuldet_set_allowed_feed(const char *os_name, const char *os_version, update_node **updates, vu_feed *agent_dist) {
    vu_feed retval = FEED_UNKNOWN;
    int i, j, k;

    for (i = 0; i < OS_SUPP_SIZE; i++) {
        if (updates[i]) {
            if (wm_vuldet_is_single_provider(updates[i]->dist_ref) || updates[i]->old_config) {
                if (updates[i]->allowed_os_name) {
                    int j;
                    char *allowed_os;
                    char *allowed_ver;
                    for (allowed_os = *updates[i]->allowed_os_name, allowed_ver = *updates[i]->allowed_os_ver, j = 0; allowed_os; ++j) {
                        if (strcasestr(os_name, allowed_os) && strcasestr(os_version, allowed_ver)) {
                            retval = updates[i]->dist_tag_ref;
                            *agent_dist = updates[i]->dist_ref;
                            i = OS_SUPP_SIZE;
                            break;
                        }
                        allowed_os = updates[i]->allowed_os_name[j];
                        allowed_ver = updates[i]->allowed_os_ver[j];
                    }
                }
            } else {
                if (updates[i]->allowed_multios_src_name) {
                    for (j = 0; updates[i]->allowed_multios_src_name[j]; j++) {
                        for (k = 0; updates[i]->allowed_multios_src_name[j][k]; k++) {
                            if (strcasestr(os_name, updates[i]->allowed_multios_src_name[j][k]) &&
                                    strcasestr(os_version, updates[i]->allowed_multios_src_ver[j][k])) {
                                int dist_tag_ref;
                                int dist_ref;

                                if (wm_vuldet_get_dist_ref(updates[i]->allowed_multios_dst_name[j], updates[i]->allowed_multios_dst_ver[j],
                                        &dist_ref, &dist_tag_ref)) {
                                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INVALID_TRANSLATION_OS, updates[i]->allowed_multios_dst_name[j], updates[i]->allowed_multios_dst_ver[j]);
                                    goto end;
                                }

                                retval = dist_tag_ref;
                                *agent_dist = dist_ref;
                                goto end;
                            }
                        }
                    }
                }
            }
        }
    }

end:
    return retval;
}

int wm_vuldet_check_update_period(update_node *upd) {
    return upd && (!upd->last_update || (upd->interval != WM_VULNDETECTOR_ONLY_ONE_UPD && (upd->last_update + (time_t) upd->interval) < time(NULL)));
}
int wm_vuldet_sync_feed(update_node *upd) {
    int need_update = 1;
    return wm_vuldet_fetch_feed(upd, &need_update) == OS_INVALID || (need_update && wm_vuldet_index_feed(upd));
}

int wm_vuldet_create_file(const char *path, const char *source) {
    const char *ROOT = "root";
    const char *sql;
    const char *tail;
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;
    uid_t uid;
    gid_t gid;

    // Open the database file, or create it
    if (sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CREATE_DB_ERROR);
        return wm_vuldet_sql_error(db, stmt);
    }

    // Load the tables schema
    for (sql = source; sql && *sql; sql = tail) {
        if (wm_vuldet_prepare(db, sql, -1, &stmt, &tail) != SQLITE_OK) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CREATE_DB_ERROR);
            return wm_vuldet_sql_error(db, stmt);
        }

        result = wm_vuldet_step(stmt);

        switch (result) {
        case SQLITE_MISUSE:
        case SQLITE_ROW:
        case SQLITE_DONE:
            break;
        default:
            return wm_vuldet_sql_error(db, stmt);
        }

        wdb_finalize(stmt);
    }

    sqlite3_close_v2(db);

    uid = Privsep_GetUser(ROOT);
    gid = Privsep_GetGroup(GROUPGLOBAL);

    if (uid == (uid_t) - 1 || gid == (gid_t) - 1) {
        mterror(WM_VULNDETECTOR_LOGTAG, USER_ERROR, ROOT, GROUPGLOBAL, strerror(errno), errno);
        return OS_INVALID;
    }

    if (chown(path, uid, gid) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, CHOWN_ERROR, path, errno, strerror(errno));
        return OS_INVALID;
    }

    if (chmod(path, 0660) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, CHMOD_ERROR, path, errno, strerror(errno));
        return OS_INVALID;
    }

    return 0;
}

int wm_vuldet_prepare(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **stmt, const char **pzTail) {
    int attempts;
    int result;
    for (attempts = 0; (result = sqlite3_prepare_v2(db, zSql, nByte, stmt, pzTail)) == SQLITE_BUSY; attempts++) {
        if (attempts == MAX_SQL_ATTEMPTS) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MAX_ACC_EXC);
            return OS_INVALID;
        }
    }
    return result;
}

int wm_vuldet_step(sqlite3_stmt *stmt) {
    int attempts;
    int result;
    for (attempts = 0; (result = sqlite3_step(stmt)) == SQLITE_BUSY; attempts++) {
        if (attempts == MAX_SQL_ATTEMPTS) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MAX_ACC_EXC);
            return OS_INVALID;
        }
    }
    return result;
}

/*
* This function should be taken as the version comparison entry point
*/
int wm_checks_package_vulnerability(char *version_a, const char *operation, const char *version_b, version_type vertype) {
    int size;
    int epoch, c_epoch;
    char version_cl[KEY_SIZE];
    char cversion_cl[KEY_SIZE];
    char *version_it, *release_it;
    char *cversion_it, *crelease_it;
    struct pkg_version *package_version;
    struct pkg_version *package_feed_version;
    enum pkg_relation feed_condition;
    int8_t ret = 0;

    if (version_b && strcmp(version_b, version_null)) {
        // Copy the original values
        if (size = snprintf(version_cl, KEY_SIZE, "%s", version_a), size >= KEY_SIZE) {
            return OS_INVALID;
        }
        if (size = snprintf(cversion_cl, KEY_SIZE, "%s", version_b), size >= KEY_SIZE) {
            return OS_INVALID;
        }

        // Check EPOCH
        // If the epoch is not present in the version and we are comparing NVD versions
        // we assign -1 to indicate that it shouldn't be compared
        if (version_it = strchr(version_cl, ':'), version_it) {
            *(version_it++) = '\0';
            epoch = strtol(version_cl, NULL, 10);
        } else {
            version_it = version_cl;
            epoch = (VER_TYPE_NVD != vertype) ? 0 : -1;
        }
        if (cversion_it = strchr(cversion_cl, ':'), cversion_it) {
            *(cversion_it++) = '\0';
            c_epoch = strtol(cversion_cl, NULL, 10);
        } else {
            cversion_it = cversion_cl;
            c_epoch = (VER_TYPE_NVD != vertype) ? 0 : -1;
        }

        // Separate the version from the revision
        if (release_it = strchr(version_it, '-'), release_it) {
            if (*(release_it++) = '\0', *release_it == '\0') {
                release_it = NULL;
            }
        }

        if (crelease_it = strchr(cversion_it, '-'), crelease_it) {
            if (*(crelease_it++) = '\0', *crelease_it == '\0') {
                crelease_it = NULL;
            }
        }
        os_calloc(1, sizeof(struct pkg_version), package_version);
        os_calloc(1, sizeof(struct pkg_version), package_feed_version);
        package_version->epoch = epoch;
        package_version->version = version_it;
        package_version->revision = release_it;
        package_feed_version->epoch = c_epoch;
        package_feed_version->version = cversion_it;
        package_feed_version->revision = crelease_it;

        if (!strcmp(operation, vu_package_comp[VU_COMP_L])) {
            feed_condition = PKG_RELATION_LT;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_LE])) {
            feed_condition = PKG_RELATION_LE;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_G])) {
            feed_condition = PKG_RELATION_GT;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_GE])) {
            feed_condition = PKG_RELATION_GE;
        } else if (!strcmp(operation, vu_package_comp[VU_COMP_EQ])) {
            feed_condition = PKG_RELATION_EQ;
        } else {
            ret = VU_ERROR_CMP;
            goto clean;
        }

        if(pkg_version_relate(package_version, feed_condition, package_feed_version, vertype)) {
            ret = VU_VULNERABLE;
        } else {
            ret = VU_NOT_VULNERABLE;
        }

    clean:
        os_free(package_version);
        os_free(package_feed_version);
        return ret;
    }

    return VU_NOT_FIXED;
}

char *wm_vuldet_build_url(char *pattern, char *value) {
    size_t size;
    char *retval;

    os_calloc(VUL_BUILD_REF_MAX + 1, sizeof(char), retval);
    size = snprintf(retval, VUL_BUILD_REF_MAX, pattern, value);
    os_realloc(retval, size + 1, retval);

    return retval;
}

int wm_vuldet_compare_pkg(cve_vuln_pkg *Pkg1, cve_vuln_pkg *Pkg2) {

    if (!(!Pkg1->src_name && !Pkg2->src_name) &&
        !(Pkg1->src_name && Pkg2->src_name &&
        !strcmp(Pkg1->src_name, Pkg2->src_name))) { // Source name
        return -1;
    }

    if (!(!Pkg1->bin_name && !Pkg2->bin_name) &&
        !(Pkg1->bin_name && Pkg2->bin_name &&
        !strcmp(Pkg1->bin_name, Pkg2->bin_name))) { // Binary name
        return -1;
    }

    if (!(!Pkg1->version && !Pkg2->version) &&
        !(Pkg1->version && Pkg2->version &&
        !strcmp(Pkg1->version, Pkg2->version))) { // Version
        return -1;
    }

    /* If only the architecture differs and one of them is 'noarch',
    *  label them as equals too.  */
    if (!(!Pkg1->arch && !Pkg2->arch) && // Architecture
        (Pkg1->arch && Pkg2->arch && strcmp("noarch", Pkg1->arch) &&
        strcmp("noarch", Pkg2->arch) && strcmp(Pkg1->arch, Pkg2->arch))) {
        return -1;
    }

    if (Pkg1->nvd_cond && Pkg2->nvd_cond &&
        Pkg1->nvd_cond->id != Pkg2->nvd_cond->id) { // ID from table NVD_CVE_MATCH
        return -1;
    }

    return 0;
}

int wm_vuldet_add_cve_node(cve_vuln_pkg *newPkg, const char *cve, OSHash *cve_table) {
    int status = 0;
    int found = 0;
    cve_vuln_pkg *lastPkg = NULL;
    cve_vuln_pkg *tmpPkg  = NULL;

    if (status = OSHash_Add(cve_table, cve, (void *)newPkg), !status) {
        return -1;
    }

    // If the cve node already exists, add the pkg to the end of the linked list.
    if (status == 1) {
        if (tmpPkg = (cve_vuln_pkg *)OSHash_Get(cve_table, cve), !tmpPkg) {
            return -1;
        }

        do {
            if (!wm_vuldet_compare_pkg(tmpPkg, newPkg)) { // Same package?
                if (!tmpPkg->nvd_cond && newPkg->nvd_cond) {
                    os_calloc(1, sizeof(cve_vuln_cond_NVD), tmpPkg->nvd_cond);
                    memcpy(tmpPkg->nvd_cond, newPkg->nvd_cond, sizeof(cve_vuln_cond_NVD));

                    if (newPkg->nvd_cond->start_version) {
                        os_strdup(newPkg->nvd_cond->start_version, tmpPkg->nvd_cond->start_version);
                    }
                    if (newPkg->nvd_cond->end_version) {
                        os_strdup(newPkg->nvd_cond->end_version, tmpPkg->nvd_cond->end_version);
                    }
                    if (newPkg->nvd_cond->operator) {
                        os_strdup(newPkg->nvd_cond->operator, tmpPkg->nvd_cond->operator);
                    }
                }

                if (!tmpPkg->vuln_cond && newPkg->vuln_cond) {
                    os_calloc(1, sizeof(cve_vuln_cond), tmpPkg->vuln_cond);
                    memcpy(tmpPkg->vuln_cond, newPkg->vuln_cond, sizeof(cve_vuln_cond));

                    if (newPkg->vuln_cond->state) {
                        os_strdup(newPkg->vuln_cond->state, tmpPkg->vuln_cond->state);
                    }
                    if (newPkg->vuln_cond->operation) {
                        os_strdup(newPkg->vuln_cond->operation, tmpPkg->vuln_cond->operation);
                    }
                    if (newPkg->vuln_cond->operation_value) {
                        os_strdup(newPkg->vuln_cond->operation_value, tmpPkg->vuln_cond->operation_value);
                    }
                    if (newPkg->vuln_cond->condition) {
                        os_strdup(newPkg->vuln_cond->condition, tmpPkg->vuln_cond->condition);
                    }
                }

                tmpPkg->feed |= newPkg->feed; // Update the feed field with the new source.

                found = 1; // Package duplicated
            }
            lastPkg = tmpPkg; // Save last known entry within the linked list.
            tmpPkg = tmpPkg->next;
        } while (tmpPkg);

        if (!found) {
            lastPkg->next = newPkg;
        }
    }

    return found;
}

void wm_vuldet_free_cve_node(void *data){
    if (data) {
        cve_vuln_pkg *pkg  = (cve_vuln_pkg *) data;
        cve_vuln_pkg *next = NULL;
        do { // Free each package in the linked list.
            next = pkg->next;
            os_free(pkg->bin_name);
            os_free(pkg->src_name);
            os_free(pkg->arch);
            os_free(pkg->version);

            if (pkg->nvd_cond) {
                os_free(pkg->nvd_cond->operator);
                os_free(pkg->nvd_cond->end_version);
                os_free(pkg->nvd_cond->start_version);
                os_free(pkg->nvd_cond);
            }

            if (pkg->vuln_cond) {
                os_free(pkg->vuln_cond->state);
                os_free(pkg->vuln_cond->operation);
                os_free(pkg->vuln_cond->operation_value);
                os_free(pkg->vuln_cond->condition);
                os_free(pkg->vuln_cond);
            }
            os_free(pkg);

            pkg = next;
        } while (pkg);
    }
}

void wm_vuldet_free_report(vu_report *report) {
    os_free(report->cve);
    os_free(report->title);
    os_free(report->rationale);
    os_free(report->published);
    os_free(report->updated);
    os_free(report->assigner);
    os_free(report->cve_version);
    // Impact
    os_free(report->severity);
    wm_vuldet_free_cv_scoring_system(report->cvss2);
    wm_vuldet_free_cv_scoring_system(report->cvss3);
    os_free(report->cwe);
    // References
    free_strarray(report->advisories);
    free_strarray(report->bugzilla_references);
    free_strarray(report->references);
    free_strarray(report->ref_sources);
    // Reported software
    os_free(report->software);
    os_free(report->source);
    os_free(report->generated_cpe);
    os_free(report->version);
    os_free(report->operation);
    os_free(report->operation_value);
    os_free(report->condition);
    os_free(report->arch);
    // Agent data
    os_free(report->agent_id);
    os_free(report->agent_name);
    os_free(report->agent_ip);
    // Metadata
    os_free(report);
}

void wm_vuldet_build_nvd_report_condition(cve_vuln_cond_NVD *nvd_cond, vu_report *report) {

    report->pending = nvd_cond->end_version ? 0 : 1;
    // building condition
    if (nvd_cond->start_version && nvd_cond->end_version) {
        const char* start = "Package";
        const char* condition1 = (nvd_cond->start_operation == START_INCLUDED) ? "greater or equal than" : "greater than";
        const char* middle = "and";
        const char* condition2 = (nvd_cond->end_operation == END_INCLUDED) ? "less or equal than" : "less than";
        size_t size = strlen(start) + 1 + strlen(condition1) + 1 + strlen(nvd_cond->start_version) + 1 +
                        strlen(middle) + 1 + strlen(condition2) + 1 + strlen(nvd_cond->end_version);
        os_calloc(size + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s %s %s %s %s %s",
        start, condition1, nvd_cond->start_version,
        middle, condition2, nvd_cond->end_version);
    }
    else if (nvd_cond->start_version) {
        const char* start = "Package";
        const char* condition = (nvd_cond->start_operation == START_INCLUDED) ? "greater or equal than" : "greater than";
        size_t size = strlen(start) + 1 + strlen(condition) + 1 + strlen(nvd_cond->start_version);
        os_calloc(size + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s %s %s",
        start, condition, nvd_cond->start_version);
    }
    else if (nvd_cond->end_version) {
        const char* start = "Package";
        const char* condition = (nvd_cond->end_operation == END_INCLUDED) ? "less or equal than" : "less than";
        size_t size = strlen(start) + 1 + strlen(condition) + 1 + strlen(nvd_cond->end_version);
        os_calloc(size + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s %s %s",
        start, condition, nvd_cond->end_version);
    }
    else {
        const char* text = "Package matches a vulnerable version";
        os_calloc(strlen(text) + 1, sizeof(char), report->condition);
        sprintf(report->condition, "%s", text);
    }

    return;
}

int wm_vuldet_fill_report_nvd_cve_info(sqlite3 *db, sqlite3_stmt *stmt, vu_report *report, int *cve_table_id) {
    // CVE info query
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_CVE_INFO_FILTER_CVE], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }
    sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);

    int step_result = wm_vuldet_step(stmt);
    if (SQLITE_ROW == step_result) {
        *cve_table_id = atoi((char *)sqlite3_column_text(stmt, 0));
        char *cwe = (char *)sqlite3_column_text(stmt, 1);
        char *assigner = (char *)sqlite3_column_text(stmt, 2);
        char *rationale = (char *)sqlite3_column_text(stmt, 3);
        char *cve_version = (char *)sqlite3_column_text(stmt, 4);
        char *published = (char *)sqlite3_column_text(stmt, 5);
        char *updated = (char *)sqlite3_column_text(stmt, 6);

        w_strdup(cwe, report->cwe);
        w_strdup(assigner, report->assigner);
        w_strdup(rationale, report->rationale);
        w_strdup(cve_version, report->cve_version);
        w_strdup(published, report->published);
        w_strdup(updated, report->updated);
    } else if (SQLITE_DONE != step_result){
        return SQLITE_ERROR;
    }

    wdb_finalize(stmt);

    return SQLITE_OK;
}

int wm_vuldet_fill_report_nvd_references(sqlite3 *db, sqlite3_stmt *stmt, int cve_table_id, vu_report *report) {
    //Query for references
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_REFERENCE], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }
    sqlite3_bind_int(stmt, 1, cve_table_id);

    int ref_count = 0;
    int step_result = 0;
    while (step_result = wm_vuldet_step(stmt), (SQLITE_ROW == step_result)) {
        char *url = (char *)sqlite3_column_text(stmt, 0);
        char *ref_source = (char *)sqlite3_column_text(stmt, 1);

        os_realloc(report->ref_sources, (ref_count + 2) * sizeof(char *), report->ref_sources);
        os_strdup(ref_source, report->ref_sources[ref_count]);
        os_realloc(report->references, (ref_count + 2) * sizeof(char *), report->references);
        os_strdup(url, report->references[ref_count]);
        ++ref_count;
        report->ref_sources[ref_count] = report->references[ref_count] = NULL;
    }

    wdb_finalize(stmt);

    if (SQLITE_ERROR == step_result)
        return SQLITE_ERROR;

    return SQLITE_OK;
}

int wm_vuldet_fill_report_nvd_scoring(sqlite3 *db, sqlite3_stmt *stmt, int cve_table_id, vu_report *report) {
    //Query for scoring
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_SCORING], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }
    sqlite3_bind_int(stmt, 1, cve_table_id);

    int step_result = 0;
    while (step_result = wm_vuldet_step(stmt), (SQLITE_ROW == step_result)) {
        cv_scoring_system **cvss;
        char *vector_string = (char *)sqlite3_column_text(stmt, 0);
        double base_score = sqlite3_column_double(stmt, 1);
        double exploitability_score = sqlite3_column_double(stmt, 2);
        double impact_score = sqlite3_column_double(stmt, 3);
        char *cvss_ver = (char *)sqlite3_column_text(stmt, 4);

        if (!cvss_ver) {
            continue;
        }

        if (!strcmp(cvss_ver, "2.0") && !report->cvss2) {
            cvss = &report->cvss2;
        } else if (!strcmp(cvss_ver, "3.0") && !report->cvss3) {
            cvss = &report->cvss3;
        } else {
            continue;
        }

        os_calloc(1, sizeof(cv_scoring_system), *cvss);
        w_strdup(vector_string, (*cvss)->vector_string);
        (*cvss)->base_score = base_score;
        (*cvss)->exploitability_score = exploitability_score;
        (*cvss)->impact_score = impact_score;
    }

    wdb_finalize(stmt);

    if (SQLITE_ERROR == step_result)
        return SQLITE_ERROR;

    if (report->cvss3) {
        if (report->cvss3->base_score >= 9) {
            os_strdup(vu_severities[VU_CRITICAL], report->severity);
        } else if (report->cvss3->base_score >= 7) {
            os_strdup(vu_severities[VU_HIGH], report->severity);
        } else if (report->cvss3->base_score >= 4) {
            os_strdup(vu_severities[VU_MEDIUM], report->severity);
        } else if (report->cvss3->base_score >= 0.1) {
            os_strdup(vu_severities[VU_LOW], report->severity);
        } else {
            os_strdup(vu_severities[VU_NONE], report->severity);
        }
    } else if (report->cvss2) {
        if (report->cvss2->base_score >= 7) {
            os_strdup(vu_severities[VU_HIGH], report->severity);
        } else if (report->cvss2->base_score >= 4) {
            os_strdup(vu_severities[VU_MEDIUM], report->severity);
        } else {
            os_strdup(vu_severities[VU_LOW], report->severity);
        }
    } else {
        os_strdup(vu_severities[VU_UNKNOWN], report->severity);
    }

    return SQLITE_OK;
}

int wm_vuldet_fill_report_oval_data(sqlite3 *db, sqlite3_stmt *stmt, agent_software *agents_it, cve_vuln_pkg *pkg, vu_report *report) {
    // As we consider the NVD as our source of trust, within this statement we will
    // add information only if that information was not already added by the NVD
    // report, or if the information is not repeated.
    const char *target;
    char *title;
    char *severity;
    char *rationale;
    char *published;
    char *updated;
    char *cvss;
    char *cvss3;
    char *cvss_vector;
    char *cvss3_vector;
    char *cwe;
    char *reference;
    char *bugzilla_reference;
    char* advisory;
    int adv_count = 0;
    int ref_count = 0;
    int bugref_count = 0;

    if (agents_it->dist == FEED_REDHAT) {
        target = vu_feed_tag[agents_it->dist];
    } else {
        target = vu_feed_tag[agents_it->dist_ver];
    }

    if (pkg->vuln_cond) {
        if (!report->operation) w_strdup(pkg->vuln_cond->operation, report->operation);
        if (!report->operation_value) w_strdup(pkg->vuln_cond->operation_value, report->operation_value);
        if (!report->condition) w_strdup(pkg->vuln_cond->condition, report->condition);
    }

    // Adding OVALs information
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_VULN_INFO], -1, &stmt, NULL) != SQLITE_OK) {
        return SQLITE_ERROR;
    }

    sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
    sqlite3_bind_text(stmt, 2, target, -1, NULL);

    if (SQLITE_ROW == wm_vuldet_step(stmt)) {
        title = (char *)sqlite3_column_text(stmt, 0);
        severity = (char *)sqlite3_column_text(stmt, 1);
        published = (char *)sqlite3_column_text(stmt, 2);
        updated = (char *)sqlite3_column_text(stmt, 3);
        rationale = (char *)sqlite3_column_text(stmt, 4);
        cvss = (char *)sqlite3_column_text(stmt, 5);
        cvss3 = (char *)sqlite3_column_text(stmt, 6);
        cvss_vector = (char *)sqlite3_column_text(stmt, 7);
        cvss3_vector = (char *)sqlite3_column_text(stmt, 8);
        cwe = (char *)sqlite3_column_text(stmt, 9);

        if (!report->title) w_strdup(title, report->title);
        if (!report->rationale) w_strdup(rationale, report->rationale);
        if (severity && agents_it->dist == FEED_REDHAT) {
            /* For RHEL/CentOS agents we trust in the severity
            from the vendor feed instead of the NVD.
            It avoids NULL severity for some vulnerabilities */
            os_free(report->severity);
            w_strdup(severity, report->severity);
        } else if (!report->severity) {
            w_strdup(severity, report->severity);
        }
        if (!report->published) w_strdup(published, report->published);
        if (!report->updated && updated) w_strdup(updated, report->updated);
        if (!report->cvss2 && cvss) {
            os_calloc(1, sizeof(cv_scoring_system), report->cvss2);
            w_strdup(cvss_vector, report->cvss2->vector_string);
            report->cvss2->base_score = strtod(cvss, NULL);
        }
        if (!report->cvss3 && cvss3) {
            os_calloc(1, sizeof(cv_scoring_system), report->cvss3);
            w_strdup(cvss3_vector, report->cvss3->vector_string);
            report->cvss3->base_score = strtod(cvss3, NULL);
        }
        if (!report->cwe) w_strdup(cwe, report->cwe);

        wdb_finalize(stmt);

        // Adding references URLs
        if (wm_vuldet_prepare(db, vu_queries[VU_REFS_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            return SQLITE_ERROR;
        }

        sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
        sqlite3_bind_text(stmt, 2, target, -1, NULL);

        for (ref_count = 0; report->references && report->references[ref_count]; ++ref_count) {
            // Do nothing. Just counting the number of references just
            // in case the NVD could have add some references URLs.
        }

        while (SQLITE_ROW == wm_vuldet_step(stmt)) {
            reference = (char *)sqlite3_column_text(stmt, 0);

            // Checking if the reference is already in the list
            int skip = 0;
            for (int i = 0; report->references && report->references[i]; ++i) {
                if (!strcmp(reference, report->references[i])) {
                    skip = 1;
                    break;
                }
            }

            if (skip) continue;

            // The reference is not in the list... adding it.
            os_realloc(report->references, (ref_count + 2) * sizeof(char *), report->references);
            os_strdup(reference, report->references[ref_count]);
            report->references[++ref_count] = NULL;
        }

        wdb_finalize(stmt);

        // Adding bugzilla references URLs
        if (wm_vuldet_prepare(db, vu_queries[VU_BUG_REFS_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            return SQLITE_ERROR;
        }

        sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
        sqlite3_bind_text(stmt, 2, target, -1, NULL);

        for (bugref_count = 0; report->bugzilla_references && report->bugzilla_references[bugref_count]; ++bugref_count) {
            // Do nothing. Just counting the number of references just
            // in case the NVD could have add some bugzilla URLs.
        }

        while (SQLITE_ROW == wm_vuldet_step(stmt)) {
            bugzilla_reference = (char *)sqlite3_column_text(stmt, 0);
            os_realloc(report->bugzilla_references, (bugref_count + 2) * sizeof(char *), report->bugzilla_references);
            os_strdup(bugzilla_reference, report->bugzilla_references[bugref_count]);
            report->bugzilla_references[++bugref_count] = NULL;
        }

        wdb_finalize(stmt);

        // Adding advisories
        if (wm_vuldet_prepare(db, vu_queries[VU_ADVISORY_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            return SQLITE_ERROR;
        }

        sqlite3_bind_text(stmt, 1, report->cve, -1, NULL);
        sqlite3_bind_text(stmt, 2, target, -1, NULL);

        for (adv_count = 0; report->advisories && report->advisories[adv_count]; ++adv_count) {
            // Do nothing. Just counting the number of references just
            // in case the NVD could have add some advisories.
        }

        while (SQLITE_ROW == wm_vuldet_step(stmt)) {
            advisory = (char *)sqlite3_column_text(stmt, 0);
            os_realloc(report->advisories, (adv_count + 2) * sizeof(char *), report->advisories);
            os_strdup(advisory, report->advisories[adv_count]);
            report->advisories[++adv_count] = NULL;
        }

        wdb_finalize(stmt);
    }

    wdb_finalize(stmt);

    return SQLITE_OK;
}

int wm_vuldet_send_agent_report(sqlite3 *db, OSHash *cve_table, agent_software *agents_it, wm_vuldet_flags *flags) {
    OSHashNode *hash_node;
    unsigned int inode_it = 0;
    sqlite3_stmt *stmt = NULL;
    vu_report *report;
    time_t start_time;
    int vuln_reported = 0;
    int vuln_reported_nvd = 0;
    int vuln_reported_vendor = 0;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_VUL_AG_SEND, atoi(agents_it->agent_id));

    start_time = time(NULL);

    hash_node = OSHash_Begin(cve_table, &inode_it);

    while(hash_node) {
        char *cve;
        cve_vuln_pkg *pkg  = NULL;
        cve_vuln_pkg *next = NULL;

        cve = hash_node->key;
        pkg = hash_node->data;

        do {
            next = pkg->next;

            if (pkg->discard) {
                pkg = next;
                continue;
            }

            if (!flags->report_kernel_os_package &&
                    (!strcmp(pkg->bin_name, PR_UBUNTU) || !strcmp(pkg->bin_name, PR_DEBIAN)
                        || !strcmp(pkg->bin_name, PR_REDHAT) || !strcmp(pkg->bin_name, PR_KERNEL))) {
                pkg = next;
                continue;
            }

            os_calloc(1, sizeof(vu_report), report);

            // Adding agent information
            w_strdup(agents_it->agent_id, report->agent_id);
            w_strdup(agents_it->agent_name, report->agent_name);
            w_strdup(agents_it->agent_ip, report->agent_ip);

            // Adding package information
            w_strdup(cve, report->cve);
            w_strdup(pkg->bin_name, report->software);
            w_strdup(pkg->src_name, report->source);
            if (!strncmp("-1:", pkg->version, 3)) {
                w_strdup((pkg->version + 3), report->version);
            } else {
                w_strdup(pkg->version, report->version);
            }
            w_strdup(pkg->arch, report->arch);

            // Adding data from NVD
            if ((pkg->feed == VU_SRC_NVD) && pkg->nvd_cond) {
                wm_vuldet_build_nvd_report_condition(pkg->nvd_cond, report);
            }

            int cve_table_id = 0;

            if (wm_vuldet_fill_report_nvd_cve_info(db, stmt, report, &cve_table_id) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_NVD_INFO_ERROR, atoi(agents_it->agent_id));
                goto error;
            }

            if (wm_vuldet_fill_report_nvd_references(db, stmt, cve_table_id, report) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_NVD_REF_ERROR, atoi(agents_it->agent_id));
                goto error;
            }

            if (wm_vuldet_fill_report_nvd_scoring(db, stmt, cve_table_id, report) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_NVD_SCORE_ERROR, atoi(agents_it->agent_id));
                goto error;
            }

            // Adding data from OVAL
            if (wm_vuldet_fill_report_oval_data(db, stmt, agents_it, pkg, report) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_OVAL_ERROR, atoi(agents_it->agent_id));
                goto error;
            }

            // Sending CVE report
            if (wm_vuldet_send_cve_report(report)) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_SEND_AGENT_REPORT_ERROR, report->cve, report->software, atoi(agents_it->agent_id));
            } else {
                if (pkg->feed & VU_SRC_NVD) {
                    vuln_reported_nvd++;
                }
                if (pkg->feed & VU_SRC_OVAL) {
                    vuln_reported_vendor++;
                }
                vuln_reported++;
            }
            wm_vuldet_free_report(report);

            pkg = next;
        } while (pkg);

        hash_node = OSHash_Next(cve_table, &inode_it, hash_node);
    }

    wdb_finalize(stmt);

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_VULN_SEND_AG_FEED, vuln_reported_nvd, atoi(agents_it->agent_id), "NVD");
    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_VULN_SEND_AG_FEED, vuln_reported_vendor, atoi(agents_it->agent_id), "vendor");
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VULN_SEND_AG, vuln_reported, atoi(agents_it->agent_id));
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start_time, "report", atoi(agents_it->agent_id));

    return 0;

error:

    if (report) {
        wm_vuldet_free_report(report);
    }

    return wm_vuldet_sql_error(db, stmt);
}

int wm_vuldet_send_cve_report(vu_report *report) {
    cJSON *alert = NULL;
    cJSON *alert_cve = NULL;
    int retval = OS_INVALID;
    int send_queue;
    char *str_json = NULL;
    char header[OS_SIZE_256 + 1];
    char alert_msg[OS_MAXSTR + 1];
    // Define time to sleep between messages sent
    int usec = 1000000 / wm_max_eps;
    char *timestamp = NULL;

    if (alert = cJSON_CreateObject(), !alert) {
        return retval;
    }

    if (alert_cve = cJSON_CreateObject(), alert_cve) {
        cJSON *j_package = NULL;
        cJSON *j_cvss = NULL;

        cJSON_AddItemToObject(alert, "vulnerability", alert_cve);

        if (j_package = cJSON_CreateObject(), !j_package) {
            goto end;
        }
        cJSON_AddItemToObject(alert_cve, "package", j_package);

        cJSON_AddStringToObject(j_package, "name", report->software);
        if (report->source) cJSON_AddStringToObject(j_package, "source", report->source);
        if (report->version && *report->version) cJSON_AddStringToObject(j_package, "version", report->version);
        if (report->generated_cpe) cJSON_AddStringToObject(j_package, "generated_cpe", report->generated_cpe);
        if (report->arch && *report->arch) cJSON_AddStringToObject(j_package, "architecture", report->arch);

        if (report->condition && *report->condition != '\0') {
            cJSON_AddStringToObject(j_package, "condition", report->condition);
        } else if (report->operation && report->operation_value) {
            os_free(report->condition);
            os_calloc(OS_SIZE_1024 + 1, sizeof(char), report->condition);
            snprintf(report->condition, OS_SIZE_1024, "Package %s %s", report->operation, report->operation_value);
            cJSON_AddStringToObject(j_package, "condition", report->condition);
        }

        if (report->cvss2 || report->cvss3) {
            cJSON *j_cvss_node;
            int i;

            if (j_cvss = cJSON_CreateObject(), !j_cvss) {
                goto end;
            }
            cJSON_AddItemToObject(alert_cve, "cvss", j_cvss);

            for (i = 0; i < 2; i++) {
                cv_scoring_system *cvss = !i ? report->cvss2 : report->cvss3;
                if (cvss) {
                    if (j_cvss_node = cJSON_CreateObject(), !j_cvss_node) {
                        goto end;
                    }
                    cJSON_AddItemToObject(j_cvss, !i ? "cvss2" : "cvss3", j_cvss_node);
                    // Although the function wm_vuldet_get_cvss could return NULL, we are not going to end in that case
                    // because some feeds, like Ubuntu or Debian, do not provide this information.
                    if (cvss->vector_string) cJSON_AddItemToObject(j_cvss_node, "vector", wm_vuldet_get_cvss(cvss->vector_string));
                    if (cvss->base_score) cJSON_AddItemToObject(j_cvss_node, "base_score", cJSON_CreateNumber(cvss->base_score));
                    if (cvss->exploitability_score) cJSON_AddItemToObject(j_cvss_node, "exploitability_score", cJSON_CreateNumber(cvss->exploitability_score));
                    if (cvss->impact_score) cJSON_AddItemToObject(j_cvss_node, "impact_score", cJSON_CreateNumber(cvss->impact_score));
                }
            }
        }

        // Set the alert body
        cJSON_AddStringToObject(alert_cve, "cve", report->cve);
        if (report->title) {
            cJSON_AddStringToObject(alert_cve, "title", report->title);
            if (report->rationale) cJSON_AddStringToObject(alert_cve, "rationale", report->rationale);
        }
        else if (report->rationale) {
            cJSON_AddStringToObject(alert_cve, "title", report->rationale);
        }
        cJSON_AddStringToObject(alert_cve, "severity", wm_vuldet_get_unified_severity(report->severity));
        if (timestamp = wm_vuldet_normalize_date(&report->published), timestamp) {
            cJSON_AddStringToObject(alert_cve, "published", timestamp);
        }
        if (timestamp = wm_vuldet_normalize_date(&report->updated), timestamp) {
            cJSON_AddStringToObject(alert_cve, "updated", timestamp);
        }
        if (report->cwe) cJSON_AddStringToObject(alert_cve, "cwe_reference", report->cwe);
        if (report->advisories) {
            cJSON *j_advisories = NULL;
            if (j_advisories = cJSON_CreateArray(), !j_advisories) {
                goto end;
            }
            int advcount;
            for (advcount = 0; report->advisories[advcount]; ++advcount)
            {
                cJSON *j_adv_item = cJSON_CreateString(report->advisories[advcount]);
                cJSON_AddItemToArray(j_advisories, j_adv_item);
            }
            cJSON_AddItemToObject(alert_cve, "advisories_ids", j_advisories);
        }
        if (report->bugzilla_references) {
            cJSON *j_bug_references = NULL;
            if (j_bug_references = cJSON_CreateArray(), !j_bug_references) {
                goto end;
            }
            int refcount;
            for (refcount = 0; report->bugzilla_references[refcount]; ++refcount)
            {
                cJSON *j_bug_item = cJSON_CreateString(report->bugzilla_references[refcount]);
                cJSON_AddItemToArray(j_bug_references, j_bug_item);
            }
            cJSON_AddItemToObject(alert_cve, "bugzilla_references", j_bug_references);
        }
        if (report->references) {
            cJSON *j_cvs_references = NULL;
            if (j_cvs_references = cJSON_CreateArray(), !j_cvs_references) {
                goto end;
            }
            int refcount;
            for (refcount = 0; report->references[refcount]; ++refcount)
            {
                cJSON *j_ref_item = cJSON_CreateString(report->references[refcount]);
                cJSON_AddItemToArray(j_cvs_references, j_ref_item);
            }
            cJSON_AddItemToObject(alert_cve, "references", j_cvs_references);
        }
        if (report->assigner) cJSON_AddStringToObject(alert_cve, "assigner", report->assigner);
        if (report->cve_version) cJSON_AddStringToObject(alert_cve, "cve_version", report->cve_version);
    } else {
        goto end;
    }

    str_json = cJSON_PrintUnformatted(alert);

    // Send an alert as a manager if there is no IP assigned
    if (report->agent_ip) {
        snprintf(header, OS_SIZE_256, VU_ALERT_HEADER, atoi(report->agent_id), report->agent_name, report->agent_ip);
        snprintf(alert_msg, OS_MAXSTR, VU_ALERT_JSON, str_json);
        send_queue = SECURE_MQ;
    } else {
        snprintf(header, OS_SIZE_256, "%s", VU_WM_NAME);
        snprintf(alert_msg, OS_MAXSTR, "%s", str_json);
        send_queue = LOCALFILE_MQ;
    }

    if (report->is_hotfix) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_HOTFIX_VUL,
            atoi(report->agent_id),
            report->cve,
            report->condition ? report->condition : "Hotfix is not installed.");
    } else {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACK_VER_VULN, report->software,
            report->version, atoi(report->agent_id), report->cve,
            report->condition && *report->condition != '\0' ? report->condition :
            "exists");
    }

    if (wm_sendmsg(usec, *vu_queue, alert_msg, header, send_queue) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, QUEUE_ERROR, DEFAULTQUEUE, strerror(errno));
        if ((*vu_queue = StartMQ(DEFAULTQUEUE, WRITE)) < 0) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, QUEUE_FATAL, DEFAULTQUEUE);
        }
    }

    retval = 0;
end:
    os_free(str_json);
    cJSON_Delete(alert);
    return retval;
}

vu_feed wm_vuldet_get_next_feed(vu_feed feed) {
    vu_feed new_feed = FEED_UNKNOWN;

    switch (feed) {
    // Ubuntu
    case FEED_FOCAL:
        new_feed = FEED_BIONIC;
        break;
    case FEED_BIONIC:
        new_feed = FEED_XENIAL;
        break;
    case FEED_XENIAL:
        new_feed = FEED_TRUSTY;
        break;
    case FEED_TRUSTY:
        new_feed = FEED_FOCAL;
        break;
    // Debian
    case FEED_BUSTER:
        new_feed = FEED_STRETCH;
        break;
    case FEED_STRETCH:
        new_feed = FEED_JESSIE;
        break;
    case FEED_JESSIE:
        new_feed = FEED_WHEEZY;
        break;
    case FEED_WHEEZY:
        new_feed = FEED_BUSTER;
        break;
    // RHEL
    case FEED_RHEL8:
        new_feed = FEED_RHEL7;
        break;
    case FEED_RHEL7:
        new_feed = FEED_RHEL6;
        break;
    case FEED_RHEL6:
        new_feed = FEED_RHEL5;
        break;
    case FEED_RHEL5:
        new_feed = FEED_RHEL8;
        break;
    // Default
    default:
        new_feed = FEED_UNKNOWN;
    }

    return new_feed;
}

int wm_vuldet_linux_rm_nvd_not_affected_packages(sqlite3 *db, agent_software *agent, const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    sqlite3_stmt *stmt = NULL;
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;

    do {
        next = pkg->next;

        if ((pkg->feed == VU_SRC_NVD) && !pkg->discard && strcmp(pkg->bin_name, PR_KERNEL) && strcmp(pkg->bin_name, PR_UBUNTU) && strcmp(pkg->bin_name, PR_DEBIAN) && strcmp(pkg->bin_name, PR_REDHAT)) {

            vu_feed feed = agent->dist_ver;

            do {
                if (wm_vuldet_prepare(db, vu_queries[VU_GET_VULN_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
                    mterror(WM_VULNDETECTOR_LOGTAG, VU_FILTER_VULN_ERROR, cve, pkg->bin_name);
                    return wm_vuldet_sql_error(db, stmt);
                }

                sqlite3_bind_text(stmt, 1, cve, -1, NULL);
                sqlite3_bind_text(stmt, 2, vu_feed_tag[feed], -1, NULL);

                if ((SQLITE_ROW == wm_vuldet_step(stmt)) && sqlite3_column_int(stmt, 0)) {

                    // We will analize only those CVEs with entries in the OVAL

                    wdb_finalize(stmt);

                    if (wm_vuldet_prepare(db, vu_queries[VU_GET_MATCHES_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
                        mterror(WM_VULNDETECTOR_LOGTAG, VU_FILTER_VULN_ERROR, cve, pkg->bin_name);
                        return wm_vuldet_sql_error(db, stmt);
                    }

                    sqlite3_bind_text(stmt, 1, cve, -1, NULL);
                    sqlite3_bind_text(stmt, 2, vu_feed_tag[feed], -1, NULL);

                    if (pkg->src_name && (agent->dist == FEED_DEBIAN || agent->dist == FEED_REDHAT)) {
                        sqlite3_bind_text(stmt, 3, pkg->src_name, -1, NULL);
                    } else {
                        sqlite3_bind_text(stmt, 3, pkg->bin_name, -1, NULL);
                    }

                    if ((SQLITE_ROW == wm_vuldet_step(stmt)) && !sqlite3_column_int(stmt, 0)) {
                        // We can discard NVD vulnerabilities in two cases:
                        // - The OVAL says that this package is patched.
                        // - The OVAL says that this binary is not affected.
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, pkg->bin_name, cve, "OVAL");
                        pkg->discard = 1;
                        (*vuln_discarded)++;
                    }

                    feed = FEED_UNKNOWN;
                }

                wdb_finalize(stmt);

                // We will check the previous versions to ensure there is no fix that has already
                // been introduced in this version at the time of publication. Also, we will check
                // the next versions to check if this version is not affected.
                feed = wm_vuldet_get_next_feed(feed);

            } while ((feed != FEED_UNKNOWN) && (feed != agent->dist_ver));
        }

        pkg = next;
    } while (pkg);

    wdb_finalize(stmt);

    return 0;
}

void wm_vuldet_linux_rm_nvd_not_dependencies_met_packages(const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;

    do {
        next = pkg->next;

        if ((pkg->feed == VU_SRC_NVD) && !pkg->discard && pkg->nvd_cond) {

            if (pkg->nvd_cond->parent == 0 && !strcmp(pkg->nvd_cond->operator, "AND")) { // Analyze children
                int child = 0;

                for (child = 0; child < MAX_RELATED_PKGS && pkg->nvd_cond->children[child]; child++) {
                    int found = 0;
                    cve_vuln_pkg *tmp = first;

                    while (tmp) {
                        if ((tmp->feed & VU_SRC_NVD) && !tmp->discard && tmp->nvd_cond && (pkg->nvd_cond->children[child] == tmp->nvd_cond->id)) {
                            found = 1;
                            break;
                        }
                        tmp = tmp->next;
                    }

                    if (!found) {
                        // If any dependency isn't installed
                        // Then the package is not vulnerable
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NO_DEP, pkg->bin_name, cve, "children", pkg->nvd_cond->children[child]);
                        pkg->discard = 1;
                        (*vuln_discarded)++;
                        break;
                    }
                }
            }

            else if (pkg->nvd_cond->parent != 0 && !strcmp(pkg->nvd_cond->operator, "OR")) { // Analyze siblings
                int sibling = 0;

                for (sibling = 0; sibling < MAX_RELATED_PKGS && pkg->nvd_cond->siblings[sibling]; sibling++) {
                    int found = 0;
                    cve_vuln_pkg *tmp = first;

                    while (tmp) {
                        if ((tmp->feed & VU_SRC_NVD) && !tmp->discard && tmp->nvd_cond && (pkg->nvd_cond->siblings[sibling] == tmp->nvd_cond->conf_id)) {
                            found = 1;
                            break;
                        }
                        tmp = tmp->next;
                    }

                    if (!found) {
                        // If any dependency isn't installed
                        // Then the package is not vulnerable
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NO_DEP, pkg->bin_name, cve, "sibling", pkg->nvd_cond->siblings[sibling]);
                        pkg->discard = 1;
                        (*vuln_discarded)++;
                        break;
                    }
                }
            }
        }

        pkg = next;
    } while (pkg);

    return;
}

void wm_vuldet_linux_rm_nvd_not_vulnerable_packages(const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;

    do {
        next = pkg->next;

        if ((pkg->feed == VU_SRC_NVD) && !pkg->discard && pkg->nvd_cond && !pkg->nvd_cond->vulnerable) {
            // Some packages are dependencies of others but aren't vulnerable
            // We won't alert those packages
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_DEP_NOT_VU, pkg->bin_name, cve);
            pkg->discard = 1;
            (*vuln_discarded)++;
        }

        pkg = next;
    } while (pkg);

    return;
}

int wm_vuldet_linux_rm_oval_not_affected_packages(sqlite3 *db, const char *cve, cve_vuln_pkg *first, int *vuln_discarded) {
    sqlite3_stmt *stmt = NULL;
    cve_vuln_pkg *pkg = first;
    cve_vuln_pkg *next = NULL;

    do {
        next = pkg->next;

        if ((pkg->feed == VU_SRC_OVAL) && !pkg->discard && pkg->vuln_cond && !strcmp(pkg->vuln_cond->state, "Unfixed")) {

            if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_MATCHES_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_FILTER_VULN_ERROR, cve, pkg->bin_name);
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, cve, -1, NULL);
            sqlite3_bind_text(stmt, 2, pkg->bin_name, -1, NULL);

            if (pkg->src_name) {
                sqlite3_bind_text(stmt, 3, pkg->src_name, -1, NULL);
            } else {
                sqlite3_bind_text(stmt, 3, pkg->bin_name, -1, NULL);
            }

            if ((SQLITE_ROW == wm_vuldet_step(stmt)) && !sqlite3_column_int(stmt, 0)) {
                // We can discard this vulnerability if the NVD says that this version is not affected
                mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, pkg->bin_name, cve, "NVD");
                pkg->discard = 1;
                (*vuln_discarded)++;
            }

            wdb_finalize(stmt);

            if (pkg->discard) {
                cve_vuln_pkg *tmp = first;
                bool discard = false;

                while (tmp) {
                    if ((tmp->feed == VU_SRC_OVAL) && !tmp->discard) {

                        if (pkg->src_name && tmp->src_name && !strcmp(pkg->src_name, tmp->src_name)) {
                            discard = true;
                        } else if (pkg->src_name && !strcmp(pkg->src_name, tmp->bin_name)) {
                            discard = true;
                        } else if (tmp->src_name && !strcmp(pkg->bin_name, tmp->src_name)) {
                            discard = true;
                        }

                        if (discard) {
                            // If the NVD says that this package isn't vulnerable,
                            // we will discard all the packages with the same source
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_AFF, tmp->bin_name, cve, "NVD");
                            tmp->discard = 1;
                            (*vuln_discarded)++;
                        }
                    }
                    tmp = tmp->next;
                }
            }
        }

        pkg = next;
    } while (pkg);

    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_linux_rm_false_positivies(sqlite3 *db, agent_software *agent, OSHash *cve_table) {
    OSHashNode *hash_node;
    unsigned int inode_it = 0;
    time_t start_time;
    int *vuln_discarded;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_VUL_AG_FIL, atoi(agent->agent_id));

    start_time = time(NULL);

    hash_node = OSHash_Begin(cve_table, &inode_it);

    os_calloc(1, sizeof(int), vuln_discarded);

    while(hash_node) {
        char *cve;
        cve_vuln_pkg *first = NULL;

        cve = hash_node->key;
        first = hash_node->data;

        // We will discard the packages already fixed by the vendor (Ubuntu/Debian/RedHat)
        if (wm_vuldet_linux_rm_nvd_not_affected_packages(db, agent, cve, first, vuln_discarded)) {
            os_free(vuln_discarded);
            return OS_INVALID;
        }

        // We will discard the packages that depend on packages not installed
        wm_vuldet_linux_rm_nvd_not_dependencies_met_packages(cve, first, vuln_discarded);

        // We will discard the packages that have vulnerable field set to 0
        wm_vuldet_linux_rm_nvd_not_vulnerable_packages(cve, first, vuln_discarded);

        // We will discard the packages not fixed by the OVAL that the NVD says they are not affected
        if (wm_vuldet_linux_rm_oval_not_affected_packages(db, cve, first, vuln_discarded)) {
            os_free(vuln_discarded);
            return OS_INVALID;
        }

        hash_node = OSHash_Next(cve_table, &inode_it, hash_node);
    }

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VULN_FILTER_AG, *vuln_discarded, atoi(agent->agent_id));
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start_time, "filter", atoi(agent->agent_id));

    os_free(vuln_discarded);

    return 0;
}

int wm_vuldet_linux_oval_vulnerabilities(sqlite3 *db, agent_software *agents_it, OSHash *cve_table) {
    sqlite3_stmt *stmt = NULL;
    const char *query;
    version_type vertype = VER_TYPE_NONE;
    cve_vuln_pkg *vuln_pkg = NULL;
    time_t start_time;
    int vuln_count = 0;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_OVAL_AG_AN, atoi(agents_it->agent_id));

    start_time = time(NULL);

    if (agents_it->dist == FEED_REDHAT) {
        query = vu_queries[VU_JOIN_RH_QUERY];
        vertype = VER_TYPE_RPM;
    } else if (agents_it->dist == FEED_DEBIAN) {
        query = vu_queries[VU_JOIN_DEBIAN_QUERY];
        vertype = VER_TYPE_DEB;
    } else {
        query = vu_queries[VU_JOIN_QUERY];
        vertype = VER_TYPE_DEB;
    }

    if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_PACKAGES_ERROR, atoi(agents_it->agent_id));
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[agents_it->dist_ver], -1, NULL);
    sqlite3_bind_int(stmt, 2,  strtol(agents_it->agent_id, NULL, 10));

    int result = wm_vuldet_step(stmt);
    switch (result) {
    case SQLITE_ROW:
        break;
    case SQLITE_DONE:
        // There is no data in the VULNERABILITIES table for this agent
        // It has to be skipped instead of being scanned against the NVD to avoid false positives
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_OVAL_UNAVAILABLE_DATA, atoi(agents_it->agent_id));
        wdb_finalize(stmt);
        return 0;
    default:
        return wm_vuldet_sql_error(db, stmt);
    }

    do {

        char condition[OS_SIZE_1024 + 1];
        char state[50];
        char *cve;
        char *package;
        char *source;
        char *version;
        char *src_version;
        char *arch;
        char *operation;
        char *operation_value;
        int v_type;

        cve = (char *)sqlite3_column_text(stmt, 0);
        package = (char *)sqlite3_column_text(stmt, 1);
        source = (char *)sqlite3_column_text(stmt, 2);
        version = (char *)sqlite3_column_text(stmt, 3);
        arch = (char *)sqlite3_column_text(stmt, 4);
        operation = (char *)sqlite3_column_text(stmt, 5);
        operation_value = (char *)sqlite3_column_text(stmt, 6);
        src_version = (char *)sqlite3_column_text(stmt, 7);

        if (!cve || !package || !version || !arch) {
            continue;
        }

        // If we have a source version, use it to find vulnerabilities.
        if (source && src_version) {
            version = src_version;
        }

        *condition = '\0';
        *state = '\0';
        if (v_type = wm_checks_package_vulnerability(version, operation, operation_value, vertype), v_type == OS_INVALID) {
            wdb_finalize(stmt);
            return OS_INVALID;
        }
        if (v_type == VU_NOT_FIXED) {
            snprintf(state, 15, "Unfixed");
            snprintf(condition, OS_SIZE_32, "Package unfixed");
            operation = "Unfixed";
            operation_value = "";
        } else if (v_type == VU_NOT_VULNERABLE) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_VULN, package, cve, version, operation, operation_value, "OVAL");
            continue;
        } else if (v_type == VU_ERROR_CMP) {
            snprintf(condition, OS_SIZE_1024, "Could not compare package versions (%s %s).", operation, operation_value);
        } else {
            snprintf(state, 15, "Fixed");
        }

        /* Add vulnerable package to CVE hash table */

        os_calloc(1, sizeof(cve_vuln_pkg), vuln_pkg);

        os_strdup(package, vuln_pkg->bin_name);
        w_strdup(source, vuln_pkg->src_name);
        os_strdup(version, vuln_pkg->version);
        os_strdup(arch, vuln_pkg->arch);

        os_calloc(1, sizeof(cve_vuln_cond), vuln_pkg->vuln_cond);

        os_strdup(state, vuln_pkg->vuln_cond->state);
        w_strdup(operation, vuln_pkg->vuln_cond->operation);
        w_strdup(operation_value, vuln_pkg->vuln_cond->operation_value);
        if (*condition) {
            os_strdup(condition, vuln_pkg->vuln_cond->condition);
        }

        vuln_pkg->feed = VU_SRC_OVAL;
        vuln_pkg->discard = 0;
        vuln_pkg->next = NULL;

        int result = wm_vuldet_add_cve_node(vuln_pkg, cve, cve_table);

        switch (result) {
        case -1:
            mterror(WM_VULNDETECTOR_LOGTAG, VU_INSERT_PACKAGE_ERROR, package, cve, version, operation, operation_value, "OVAL");
            wm_vuldet_free_cve_node(vuln_pkg);
            break;
        case 1:
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DUPLICATED_PACKAGE, package, cve, version, operation, operation_value, "OVAL");
            vuln_count++;
            wm_vuldet_free_cve_node(vuln_pkg);
            break;
        case 0:
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_INSERT, package, cve, version, operation, operation_value, "OVAL");
            vuln_count++;
            break;
        }

    } while (SQLITE_ROW == wm_vuldet_step(stmt));

    wdb_finalize(stmt);

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VULN_OVAL_AG_COUNT, vuln_count, atoi(agents_it->agent_id));
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start_time, "find OVAL", atoi(agents_it->agent_id));

    return 0;
}

int wm_vuldet_report_agent_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags) {

    int retval = OS_INVALID;
    OSHash *cve_table = NULL;

    if (!agent->info) {
        retval = 0;   // Scan next agent
        goto end;
    }

    if (agent->dist == FEED_WIN) {

        if (wm_vuldet_win_nvd_vulnerabilities(db, agent, flags)) {
            goto end;
        }
        retval = 0;

    } else {

        // To add a new node, call wm_vuldet_add_cve_node().
        // @cve_table stores cve_vuln_pkg type nodes.
        cve_table = OSHash_Create();
        if (!cve_table) {
            merror(LIST_ERROR);
            goto end;
        }
        if (!OSHash_setSize(cve_table, VU_CVE_TABLE_SIZE)) {
            merror(LIST_ERROR);
            goto end;
        }

        if (wm_vuldet_linux_oval_vulnerabilities(db, agent, cve_table)) {
            goto end;
        }
        // Until we change our RHEL feed to learn more about unfixed/unaffected
        // packages, we won't get NVD vulnerabilities as they won't be filtered
        // and there will be plenty of false positives.
        if (agent->dist != FEED_REDHAT) {
            if (wm_vuldet_linux_nvd_vulnerabilities(db, agent, cve_table)) {
                goto end;
            }
            if (wm_vuldet_linux_rm_false_positivies(db, agent, cve_table)) {
                goto end;
            }
        }
        if (wm_vuldet_send_agent_report(db, cve_table, agent, flags)) {
            goto end;
        }
        retval = 0;
    }

end:
    if (cve_table) {
        OSHash_Clean(cve_table, wm_vuldet_free_cve_node); // Free the CVE table
    }

    return retval;
}

int wm_vuldet_check_agent_vulnerabilities(agent_software *agents, wm_vuldet_flags *flags, time_t ignore_time) {
    agent_software *agents_it;
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;
    int i;

    if (!agents) {
        mtinfo(WM_VULNDETECTOR_LOGTAG, VU_AG_NO_TARGET);
        return 0;
    } else if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CVEDB_ERROR);
        return wm_vuldet_sql_error(db, stmt);
    }

    // Iterate agents to look for vulnerabilities
    for (i = 0, agents_it = agents;; i++) {

        mtinfo(WM_VULNDETECTOR_LOGTAG, VU_START_AG_AN, atoi(agents_it->agent_id));
        time_t start = time(NULL);

        // Check there is available vulnerabilities for this agent
        if (agents_it->dist != FEED_WIN) {
            result = wm_vuldet_db_empty(db, agents_it->dist_ver);
            if (result == 0) {
                // There is no data in the VULNERABILITIES table for this agent
                // It has to be skipped instead of being scanned against the NVD to avoid false positives
                mtwarn(WM_VULNDETECTOR_LOGTAG, VU_OVAL_UNAVAILABLE_DATA, atoi(agents_it->agent_id));
                if (agents_it->next) {
                    agents_it = agents_it->next;
                    continue;
                } else {
                    break;
                }
            } else if (result == OS_INVALID) {
                // DB error
                break;
            }
        }

        // Reset the tables before scanning each agent
        wm_vuldet_reset_tables(db);

        // First step: collect its software
        if (result = wm_vuldet_get_software_info(agents_it, db, ignore_time, flags), result == OS_INVALID) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_SOFTWARE_ERROR, atoi(agents_it->agent_id));
            break;
        }

        // result == 2 skips the agent
        // is used when no hotfixes are available or no packages have been marked for scanning
        if (result != 2) {
            // Second step: find and report vulnerabilities
            if (wm_vuldet_report_agent_vulnerabilities(db, agents_it, flags) < 0) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_ERROR, atoi(agents_it->agent_id), sqlite3_errmsg(db));
                break;
            }
        }

        mtinfo(WM_VULNDETECTOR_LOGTAG, VU_AGENT_FINISH, atoi(agents_it->agent_id));
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start, "scan", atoi(agents_it->agent_id));

        if (agents_it->next) {
            agents_it = agents_it->next;
        } else {
            break;
        }
    }

    // Reset the tables
    wm_vuldet_reset_tables(db);

    sqlite3_close_v2(db);
    return 0;
}

int wm_vuldet_sql_error(sqlite3 *db, sqlite3_stmt *stmt) {
    mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
    wdb_finalize(stmt);
    sqlite3_close_v2(db);
    return OS_INVALID;
}

int wm_vuldet_remove_target_table(sqlite3 *db, char *TABLE, const char *target) {
    sqlite3_stmt *stmt = NULL;
    char sql[MAX_QUERY_SIZE];
    size_t size;

    if (size = snprintf(sql, MAX_QUERY_SIZE, vu_queries[VU_REMOVE_OS], TABLE), sql[size - 1] != ';') {
        sqlite3_close_v2(db);
        return OS_INVALID;
    }

    if (wm_vuldet_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, target, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_update_feed(update_node *upd) {
    int result;
    int pages_fail = 0;
    char feed_updated = 0;

    if (!upd->multi_path && (upd->dist_ref == FEED_NVD || upd->dist_ref == FEED_REDHAT)) {
        int start;
        int end;

        if (!upd->multi_url) { // Online update for Redhat and NVD providers
            time_t n_date;
            struct tm *t_date;

            n_date = time(NULL);
            t_date = gmtime(&n_date);

            // Set the starting and ending point for each provider
            start = upd->dist_ref == FEED_NVD ? upd->update_from_year : 1;
            end = upd->dist_ref == FEED_NVD ? t_date->tm_year + 1900 : RED_HAT_REPO_MAX_REQ_ITS;
        } else { // Offline update from a custom repo for Redhat and NVD providers
            start = upd->multi_url_start;
            end = upd->multi_url_end;
        }

        // If it is a Red Hat update, we need to clean the database before we start
        if (upd->dist_ref == FEED_REDHAT) {
            wm_vuldet_clean_rh(NULL);
        }

        for (upd->update_it = start; upd->update_it <= end; (upd->update_it)++) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, upd->dist_ref == FEED_NVD ? VU_UPDATING_NVD_YEAR : VU_UPDATING_RH_YEAR, upd->update_it);
            if (result = wm_vuldet_sync_feed(upd), result) {
                // Feed synchronization failed
                if (upd->dist_ref == FEED_NVD) {
                    wm_vuldet_clean_nvd_year(NULL, upd->update_it);
                } else if (upd->dist_ref == FEED_REDHAT) {
                    wm_vuldet_clean_rh(NULL);
                }
                return OS_INVALID;
            } else {
                if (upd->dist_ref == FEED_REDHAT) {
                    switch (upd->update_state) {
                        case VU_FINISH_FETCH: // The last page has been reached
                            return 0;
                        case VU_TRY_NEXT_PAGE: // The maximum number of possible attempts for a page has been exhausted, the following page will be attempted
                            if (pages_fail == RED_HAT_REPO_MAX_FAIL_ITS) { // The allowed number of failed pages has been exhausted. The feed will not be updated.
                                mterror(WM_VULNDETECTOR_LOGTAG, VU_RH_REQ_FAIL_MAX, RED_HAT_REPO_MAX_FAIL_ITS);
                                wm_vuldet_clean_rh(NULL);
                                return OS_INVALID;
                            }
                            pages_fail++;
                        break;
                        case VU_INV_FEED:
                            wm_vuldet_clean_rh(NULL);
                            return OS_INVALID;
                        default:
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DOWNLOAD_PAGE_SUC, upd->update_it);
                            feed_updated = 1;
                    }
                }
            }
        }

        if (upd->dist_ref == FEED_REDHAT && !feed_updated) {
            return OS_INVALID;
        }
    } else {
        // single-providers -> offline update (multi_path only)
        // multi-providers -> online and offline update
        return wm_vuldet_sync_feed(upd);
    }
    return 0;
}

int wm_vuldet_debian_set_packages_ignore(sqlite3 *db, const char *target, const long timeout) {
    sqlite3_stmt *stmt = NULL;
    sqlite3_stmt *stmt2 = NULL;
    cJSON *deb_json = NULL;

    if (deb_json = wm_vuldet_get_debian_status_feed(timeout), !deb_json) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_GET_DEB_STATUS_FEED, DEBIAN_REPO_STATUS);
        return 0;
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_CVE_UNFIXED], -1, &stmt, NULL) != SQLITE_OK) {
        cJSON_Delete(deb_json);
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, target, -1, NULL);
    sqlite3_bind_text(stmt, 2, version_null, -1, NULL);

    while (SQLITE_ROW == wm_vuldet_step(stmt)) {
        cJSON *package_json = NULL;
        cJSON *cve_json = NULL;
        cJSON *releases_json = NULL;
        cJSON *target_json = NULL;
        cJSON *status_json = NULL;
        cJSON *urgency_json = NULL;
        cJSON *reason_json = NULL;
        char *cve = NULL;
        char *package = NULL;
        char *target_lower = NULL;
        int ignore = 0;

        cve = (char *)sqlite3_column_text(stmt, 0);
        package = (char *)sqlite3_column_text(stmt, 1);
        target_lower = w_tolower_str(target);

        if (package_json = cJSON_GetObjectItem(deb_json, package), !package_json) {
            os_free(target_lower);
            continue;
        }
        if (cve_json = cJSON_GetObjectItem(package_json, cve), !cve_json) {
            os_free(target_lower);
            continue;
        }
        if (releases_json = cJSON_GetObjectItem(cve_json, "releases"), !releases_json) {
            os_free(target_lower);
            continue;
        }
        if (target_json = cJSON_GetObjectItem(releases_json, target_lower), !target_json) {
            os_free(target_lower);
            continue;
        }
        if (status_json = cJSON_GetObjectItem(target_json, "status"), !status_json) {
            os_free(target_lower);
            continue;
        }
        urgency_json = cJSON_GetObjectItem(target_json, "urgency");
        reason_json = cJSON_GetObjectItem(target_json, "nodsa_reason");

        if (!strcmp(status_json->valuestring, "resolved")) {
            ignore = 2;
        } else if (!strcmp(status_json->valuestring, "undetermined")) {
            ignore = 1;
        } else {
            if (urgency_json && !strcmp(urgency_json->valuestring, "end-of-life")) {
                ignore = 2;
            } else if (reason_json) {
                if (!strcmp(reason_json->valuestring, "ignored")) {
                    ignore = 2;
                } else if (!strcmp(reason_json->valuestring, "postponed")) {
                    ignore = 1;
                }
            }
        }

        if (ignore) {
            // Update state to ignore
            if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_CVE_IGNORE], -1, &stmt2, NULL) != SQLITE_OK) {
                wdb_finalize(stmt);
                os_free(target_lower);
                cJSON_Delete(deb_json);
                return wm_vuldet_sql_error(db, stmt2);
            }

            sqlite3_bind_int(stmt2, 1, ignore);
            sqlite3_bind_text(stmt2, 2, package, -1, NULL);
            sqlite3_bind_text(stmt2, 3, cve, -1, NULL);
            sqlite3_bind_text(stmt2, 4, target, -1, NULL);

            if (wm_vuldet_step(stmt2) != SQLITE_DONE) {
                wdb_finalize(stmt);
                os_free(target_lower);
                cJSON_Delete(deb_json);
                return wm_vuldet_sql_error(db, stmt2);
            }

            wdb_finalize(stmt2);
        }

        os_free(target_lower);
    }

    wdb_finalize(stmt);
    cJSON_Delete(deb_json);

    return 0;
}


int wm_vuldet_insert(wm_vuldet_db *parsed_oval, update_node *update) {
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;
    const char *query;
    oval_metadata *met_it = &parsed_oval->metadata;
    vulnerability *vul_it = parsed_oval->vulnerabilities;
    rh_vulnerability *rvul_it = parsed_oval->rh_vulnerabilities;
    info_state *state_it = parsed_oval->info_states;
    info_obj *obj_it = parsed_oval->info_objs;
    info_test *test_it = parsed_oval->info_tests;
    info_cve *info_it = parsed_oval->info_cves;
    cpe_list *cpes_it = parsed_oval->nvd_cpes;
    nvd_vulnerability *nvd_it = parsed_oval->nvd_vulnerabilities;
    vu_cpe_dic *w_cpes_it = parsed_oval->w_cpes;
    vu_msu_entries *msu_it = &parsed_oval->msu;
    variables *vars_it = parsed_oval->vars;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);

    switch (update->dist_ref) {
        case FEED_UBUNTU:
        case FEED_DEBIAN:
            if (wm_vuldet_remove_target_table(db, CVE_TABLE, parsed_oval->OS)      ||
                wm_vuldet_remove_target_table(db, METADATA_TABLE, parsed_oval->OS) ||
                wm_vuldet_remove_target_table(db, CVE_INFO_TABLE, parsed_oval->OS) ||
                wm_vuldet_remove_target_table(db, VARIABLES_TABLE, parsed_oval->OS)) {
                return OS_INVALID;
            }
        break;
        case FEED_CPEW:
            if (wm_vuldet_clean_wcpe(db)) {
                return OS_INVALID;
            }
        break;
        case FEED_NVD: case FEED_MSU: case FEED_REDHAT:
        break;
        default:
            sqlite3_close_v2(db);
            return OS_INVALID;
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_VU);

    if (cpes_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_CPES_SEC);

        if (wm_vuldet_insert_cpe_db(db, cpes_it, 1)){
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CPES_INSERT_ERROR);
            wm_vuldet_free_cpe_list(cpes_it);
            free(cpes_it);
            sqlite3_close_v2(db);
            return OS_INVALID;
        }
        wm_vuldet_free_cpe_list(cpes_it);
        free(cpes_it);
    }

    if (w_cpes_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_CPES_DIC);

        if (wm_vuldet_insert_cpe_dic(db, w_cpes_it)){
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CPES_INSERT_ERROR);
            return OS_INVALID;
        }
        free(w_cpes_it);
    }

    if (msu_it->vul) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_MSU);

        if (wm_vuldet_insert_MSU(db, msu_it)){
            return OS_INVALID;
        }
    }

    if (nvd_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_NVD_SEC);
        if (wm_vuldet_index_nvd(db, update, nvd_it)) {
            return OS_INVALID;
        }
        parsed_oval->nvd_vulnerabilities = NULL;
    }

    // Adds the vulnerabilities
    while (vul_it) {
        // If you do not have this field, it has been discarded by the preparser and the OS is not affected
        if (vul_it->state_id) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CVE], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, vul_it->cve_id, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, NULL, -1, NULL);
            sqlite3_bind_text(stmt, 4, vul_it->package_name ? vul_it->package_name : vul_it->state_id, -1, NULL);
            sqlite3_bind_text(stmt, 5, vul_it->state_id, -1, NULL);
            sqlite3_bind_text(stmt, 6, NULL, -1, NULL);
            sqlite3_bind_int(stmt, 7, 0);
            sqlite3_bind_int(stmt, 8, vul_it->ignore);

            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }

        vulnerability *vul_aux = vul_it;
        vul_it = vul_it->prev;
        free(vul_aux->cve_id);
        free(vul_aux->state_id);
        free(vul_aux->package_name);
        free(vul_aux);
    }

    if (rvul_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_RH_SEC);
        if (wm_vuldet_index_redhat(db, update, rvul_it)) {
            return OS_INVALID;
        }
        parsed_oval->rh_vulnerabilities = NULL;
    }

    if (test_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_TEST_SEC, update->dist_ext);
    }

    // Links vulnerabilities to their conditions
    while (test_it) {
        if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_CVE], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, test_it->obj, -1, NULL);
        sqlite3_bind_text(stmt, 2, test_it->state, -1, NULL);
        sqlite3_bind_text(stmt, 3, test_it->id, -1, NULL);

        result = wm_vuldet_step(stmt);

        switch (result) {
        case SQLITE_DONE:
            break;
        case SQLITE_CONSTRAINT:
            wdb_finalize(stmt);

            if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_CVE_NOT_FIXED], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, test_it->obj, -1, NULL);
            sqlite3_bind_text(stmt, 2, vu_package_comp[VU_COMP_L], -1, NULL);
            sqlite3_bind_text(stmt, 3, version_null, -1, NULL);
            sqlite3_bind_text(stmt, 4, test_it->id, -1, NULL);

            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            break;
        default:
            return wm_vuldet_sql_error(db, stmt);
        }

        wdb_finalize(stmt);

        info_test *test_aux = test_it;
        test_it = test_it->prev;
        free(test_aux->id);
        free(test_aux->state);
        free(test_aux->obj);
        free(test_aux);
    }

    if (state_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_VU_CO, update->dist_ext);

        sqlite3_exec(db, vu_queries[VU_REMOVE_UNUSED_VULS], NULL, NULL, NULL);
    }

    // Sets the OVAL operators and values
    while (state_it) {
        query = vu_queries[VU_UPDATE_CVE_VAL];
        if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, state_it->operation, -1, NULL);

        // If the epoch is 0, we don't save it because it is not necessary
        if (!strcmp(state_it->operation_value, version_null) || strncmp("0:", state_it->operation_value, 2)) {
            sqlite3_bind_text(stmt, 2, state_it->operation_value, -1, NULL);
        } else {
            sqlite3_bind_text(stmt, 2, (state_it->operation_value + 2), -1, NULL);
        }

        sqlite3_bind_text(stmt, 3, state_it->id, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }
        wdb_finalize(stmt);

        info_state *state_aux = state_it;
        state_it = state_it->prev;
        free(state_aux->id);
        free(state_aux->operation);
        free(state_aux->operation_value);
        free(state_aux->arch_value);
        free(state_aux);
    }

    if (obj_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_PACK_NAME, update->dist_ext);
    }

    // Sets the OVAL package name
    while (obj_it) {
        char *normalized_name = NULL;

        if (obj_it->obj) {
            if (result = wm_vuldet_prepare(db, vu_queries[VU_UPDATE_CVE_PACK], -1, &stmt, NULL), result != SQLITE_OK && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            normalized_name = w_tolower_str(obj_it->obj);
            sqlite3_bind_text(stmt, 1, normalized_name, -1, NULL);
            sqlite3_bind_int(stmt, 2, obj_it->need_vars);
            sqlite3_bind_text(stmt, 3, obj_it->id, -1, NULL);
        } else {
            if (result = wm_vuldet_prepare(db, vu_queries[VU_REMOVE_UNUSED_ID], -1, &stmt, NULL), result != SQLITE_OK && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, obj_it->id, -1, NULL);
        }

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
            os_free(normalized_name);
            return wm_vuldet_sql_error(db, stmt);
        }

        wdb_finalize(stmt);
        os_free(normalized_name);

        info_obj *obj_aux = obj_it;
        obj_it = obj_it->prev;
        free(obj_aux->id);
        free(obj_aux->obj);
        free(obj_aux);
    }

    if (vars_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INS_VARIABLES, update->dist_ext);
    }

    // Sets the OVAL variables
    while (vars_it) {
        char *normalized_id = NULL;
        query = vu_queries[VU_INSERT_VARIABLES];

        if (vars_it->id) {
            int j;
            normalized_id = w_tolower_str(vars_it->id);

            for (j = 0; vars_it->values[j]; j++) {
                char *normalized_name = NULL;
                if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
                    os_free(normalized_id);
                    return wm_vuldet_sql_error(db, stmt);
                }
                sqlite3_bind_text(stmt, 1, normalized_id, -1, NULL);
                normalized_name = w_tolower_str(vars_it->values[j]);
                sqlite3_bind_text(stmt, 2, normalized_name, -1, NULL);
                sqlite3_bind_text(stmt, 3, parsed_oval->OS, -1, NULL);
                if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                    os_free(normalized_id);
                    os_free(normalized_name);
                    return wm_vuldet_sql_error(db, stmt);
                }
                wdb_finalize(stmt);
                os_free(normalized_name);
            }
        }
        os_free(normalized_id);

        variables *var_aux = vars_it;
        vars_it = vars_it->prev;
        free(var_aux->id);
        w_FreeArray(var_aux->values);
        free(var_aux->values);
        free(var_aux);
    }

    // Update the ignore status of the Debian packages
    if ((update->dist_ref == FEED_DEBIAN) && (wm_vuldet_debian_set_packages_ignore(db, vu_feed_tag[update->dist_tag_ref], update->timeout) == OS_INVALID)) {
        return wm_vuldet_sql_error(db, stmt);
    }

    if (info_it) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_VU_INFO, update->dist_ext);
    }

    while (info_it) {
        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CVE_INFO], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
        sqlite3_bind_text(stmt, 2, info_it->title, -1, NULL);
        sqlite3_bind_text(stmt, 3, (info_it->severity) ? info_it->severity : vu_severities[VU_UNKNOWN], -1, NULL);
        sqlite3_bind_text(stmt, 4, info_it->published, -1, NULL);
        sqlite3_bind_text(stmt, 5, info_it->updated, -1, NULL);
        sqlite3_bind_text(stmt, 6, parsed_oval->OS, -1, NULL);
        sqlite3_bind_text(stmt, 7, info_it->description, -1, NULL);
        sqlite3_bind_text(stmt, 8, info_it->cvss, -1, NULL);
        sqlite3_bind_text(stmt, 9, info_it->cvss_vector, -1, NULL);
        sqlite3_bind_text(stmt, 10, info_it->cvss3, -1, NULL);
        sqlite3_bind_text(stmt, 11, info_it->cvss3_vector, -1, NULL);
        sqlite3_bind_text(stmt, 12, info_it->cwe, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }
        wdb_finalize(stmt);

        // Saving the references corresponding to the current CVE
        int j;
        for (j = 0; info_it->refs && (j < info_it->refs->elements) && info_it->refs->values[j]; j++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_REF_INFO], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, info_it->refs->values[j], -1, NULL);
            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }

        if (info_it->refs)
            free_strarray(info_it->refs->values);

        // Saving the bugzilla references corresponding to the current CVE
        for (j = 0; info_it->bugzilla_references && (j < info_it->bugzilla_references->elements) && info_it->bugzilla_references->values[j]; j++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_BUG_REF_INFO], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, info_it->bugzilla_references->values[j], -1, NULL);
            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }

        if (info_it->bugzilla_references)
            free_strarray(info_it->bugzilla_references->values);

        // Saving the advisories corresponding to the current CVE
        for (j = 0; info_it->advisories && (j < info_it->advisories->elements) && info_it->advisories->values[j]; j++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_ADVISORY_INFO], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_text(stmt, 1, info_it->cveid, -1, NULL);
            sqlite3_bind_text(stmt, 2, parsed_oval->OS, -1, NULL);
            sqlite3_bind_text(stmt, 3, info_it->advisories->values[j], -1, NULL);
            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }

        if (info_it->advisories)
            free_strarray(info_it->advisories->values);

        info_cve *info_aux = info_it;
        info_it = info_it->prev;
        free(info_aux->cveid);
        free(info_aux->title);
        free(info_aux->severity);
        free(info_aux->published);
        free(info_aux->updated);
        free(info_aux->refs);
        free(info_aux->description);
        free(info_aux->cvss);
        free(info_aux->cvss_vector);
        free(info_aux->cvss3);
        free(info_aux->cvss3_vector);
        free(info_aux->bugzilla_references);
        free(info_aux->advisories);
        free(info_aux->cwe);
        free(info_aux);
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_METADATA], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }
    sqlite3_bind_text(stmt, 1, parsed_oval->OS, -1, NULL);
    sqlite3_bind_text(stmt, 2, met_it->product_name, -1, NULL);
    sqlite3_bind_text(stmt, 3, met_it->product_version, -1, NULL);
    sqlite3_bind_text(stmt, 4, met_it->schema_version, -1, NULL);
    sqlite3_bind_text(stmt, 5, met_it->timestamp, -1, NULL);
    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    free(met_it->product_name);
    free(met_it->product_version);
    free(met_it->schema_version);
    free(met_it->timestamp);

    sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
    sqlite3_close_v2(db);
    return 0;
}

int wm_vuldet_check_db() {
    if (wm_vuldet_create_file(CVE_DB, schema_vuln_detector_sql)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INVALID_DB_INT);
        pthread_exit(NULL);
    }
    return 0;
}

void wm_vuldet_add_rvulnerability(wm_vuldet_db *ctrl_block) {
    rh_vulnerability *new;
    os_calloc(1, sizeof(rh_vulnerability), new);

    if (ctrl_block->rh_vulnerabilities) {
        new->prev = ctrl_block->rh_vulnerabilities;
    }
    ctrl_block->rh_vulnerabilities = new;
}

void wm_vuldet_add_vulnerability_info(wm_vuldet_db *ctrl_block) {
    info_cve *new;
    os_calloc(1, sizeof(info_cve), new);

    if (ctrl_block->info_cves) {
        new->prev = ctrl_block->info_cves;
    }
    ctrl_block->info_cves = new;
}

char *wm_vuldet_oval_xml_preparser(char *path, vu_feed dist) {
    FILE *input, *output = NULL;
    char buffer[OS_MAXSTR + 1];
    parser_state state = V_OVALDEFINITIONS;
    char *found;
    char *tmp_file;
    STATIC const char *exclude_tags[] = {
        // Debian
        "oval:org.debian.oval:tst:1\"",
        "oval:org.debian.oval:tst:2\""
    };

    os_strdup(VU_FIT_TEMP_FILE, tmp_file);

    if (input = fopen(path, "r" ), !input) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_OPEN_FILE_ERROR, path);
        os_free(tmp_file);
        goto free_mem;
    } else if (output = fopen(tmp_file, "w" ), !output) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_OPEN_FILE_ERROR, tmp_file);
        os_free(tmp_file);
        goto free_mem;
    }

    while (fgets(buffer, OS_MAXSTR, input)) {
        if (dist == FEED_UBUNTU) { //5.11.1
            switch (state) {
                case V_OBJECTS:
                    if (found = strstr(buffer, "</objects>"), found) {
                        state = V_OVALDEFINITIONS;
                    }
                break;
                case V_DEFINITIONS:
                    if ((found = strstr(buffer, "is not affected"), found) &&
                        (found = strstr(buffer, "negate"), found) &&
                        strstr(found, "true")) {
                        continue;
                    } else if (strstr(buffer, "a decision has been made to ignore it")) {
                        continue;
                    } else if (found = strstr(buffer, "</definitions>"), found) {
                        state = V_OVALDEFINITIONS;
                    }
                break;
                default:
                    if (strstr(buffer, "<objects>")) {
                        state = V_OBJECTS;
                    } else if (strstr(buffer, "<definitions>")) {
                      state = V_DEFINITIONS;
                  }
            }
        } else if (dist == FEED_DEBIAN) { //5.3
            switch (state) {
                case V_OVALDEFINITIONS:
                    if (found = strstr(buffer, "?>"), found) {
                        state = V_STATES;
                    }
                    continue;
                break;
                case V_OBJECTS:
                    if (found = strstr(buffer, "</objects>"), found) {
                        state = V_STATES;
                    }
                break;
                case V_DEFINITIONS:
                    if (strstr(buffer, exclude_tags[0]) ||
                        strstr(buffer, exclude_tags[1])) {
                        continue;
                    } else if (found = strstr(buffer, "</definitions>"), found) {
                        state = V_STATES;
                    }
                break;
                default:
                    if (strstr(buffer, "<objects>")) {
                        state = V_OBJECTS;
                    } else if (strstr(buffer, "<definitions>")) {
                      state = V_DEFINITIONS;
                    } else if (strstr(buffer, "<tests>")) {
                      state = V_TESTS;
                    }
            }
        } else {
            os_free(tmp_file);
            goto free_mem;
        }
        fwrite(buffer, 1, strlen(buffer), output);
    }

free_mem:
    if (input) {
        fclose(input);
    }
    if (output) {
        fclose(output);
    }
    return tmp_file;
}

references *wm_vuldet_extract_advisories(cJSON *advisories) {
    references *advisories_data = NULL;

    if (advisories) {
        os_calloc(1, sizeof(references), advisories_data);

        for (; advisories && advisories->valuestring; advisories = advisories->next) {
            os_realloc(advisories_data->values, (advisories_data->elements + 2) * sizeof(char *), advisories_data->values);
            w_strdup(advisories->valuestring, advisories_data->values[advisories_data->elements]);
            advisories_data->values[++advisories_data->elements] = NULL;
        }
    }

    return advisories_data;
}

void wm_vuldet_adapt_title(char *title, char *cve) {
    // Remove unnecessary line jumps and  spaces
    size_t size;
    int offset;
    char *title_ofs;

    for (size = strlen(title) - 1; size > 0 && title[size] == ' '; size -= 1) {
        title[size] = '\0';
    }

    if (title[size] == '\n') {
        title[size--] = '\0';
    }

    offset = title[0] == '\n' ? 1 : 0;
    if(size > 1 && !strncmp(title + offset, cve, strlen(cve))) {
        offset += strlen(cve) + 1;
    }
    os_strdup(title + offset, title_ofs);
    strncpy(title, title_ofs, strlen(title_ofs) + 1);
    free(title_ofs);
}

int wm_vuldet_oval_xml_parser(OS_XML *xml, XML_NODE node, wm_vuldet_db *parsed_oval, update_node *update, vu_logic condition) {
    int i, j;
    int retval = 0;
    vulnerability *vuln;
    XML_NODE chld_node = NULL;
    vu_feed dist = update->dist_ref;

    static const char *XML_OVAL_DEFINITIONS = "oval_definitions";
    static const char *XML_GENERATOR = "generator";
    static const char *XML_DEFINITIONS = "definitions";
    static const char *XML_DEFINITION = "definition";
    static const char *XML_OBJECTS = "objects";
    static const char *XML_VARIABLES = "variables";
    static const char *XML_CONST_VAR = "constant_variable";
    static const char *XML_VALUE = "value";
    static const char *XML_TITLE = "title";
    static const char *XML_CLASS = "class";
    static const char *XML_VULNERABILITY = "vulnerability"; //ub 15.11.1
    static const char *XML_METADATA = "metadata";
    static const char *XML_OVAL_DEF_METADATA = "oval-def:metadata";
    static const char *XML_CRITERIA = "criteria";
    static const char *XML_REFERENCE = "reference";
    static const char *XML_REF_ID = "ref_id";
    static const char *XML_REF_URL = "ref_url";
    static const char *XML_OPERATOR = "operator";
    static const char *XML_OR = "OR";
    static const char *XML_AND = "AND";
    static const char *XML_COMMENT = "comment";
    static const char *XML_CRITERION = "criterion";
    static const char *XML_TEST_REF = "test_ref";
    static const char *XML_TESTS = "tests";
    static const char *XML_DPKG_LINUX_INFO_TEST = "linux-def:dpkginfo_test";
    static const char *XML_DPKG_LINUX_INFO_OBJ = "linux-def:dpkginfo_object";
    static const char *XML_DPKG_LINUX_INFO_DEB_OBJ = "dpkginfo_object";
    static const char *XML_DPKG_INFO_TEST = "dpkginfo_test";
    static const char *XML_ID = "id";
    static const char *XML_LINUX_STATE = "linux-def:state";
    static const char *XML_LINUX_NAME = "linux-def:name";
    static const char *XML_VAR_REF = "var_ref";
    static const char *XML_VAR_CHECK = "var_check";
    static const char *XML_LINUX_DEB_NAME = "name";
    static const char *XML_LINUX_OBJ = "linux-def:object";
    static const char *XML_LINUX_DEB_OBJ = "object";
    static const char *XML_STATE = "state";
    static const char *XML_STATE_REF = "state_ref";
    static const char *XML_OBJECT_REF = "object_ref";
    static const char *XML_STATES = "states";
    static const char *XML_DPKG_LINUX_INFO_STATE = "linux-def:dpkginfo_state";
    static const char *XML_DPKG_INFO_STATE = "dpkginfo_state";
    static const char *XML_LINUX_DEF_EVR = "linux-def:evr";
    static const char *XML_EVR = "evr";
    static const char *XML_OPERATION = "operation";
    static const char *XML_DATATYPE = "datatype";
    static const char *XML_OVAL_PRODUCT_NAME = "oval:product_name";
    static const char *XML_OVAL_PRODUCT_VERSION = "oval:product_version";
    static const char *XML_OVAL_SCHEMA_VERSION = "oval:schema_version";
    static const char *XML_OVAL_TIMESTAMP = "oval:timestamp";
    static const char *XML_ADVISORY = "advisory";
    static const char *XML_SEVERITY = "severity";
    static const char *XML_PUBLIC_DATE = "public_date";
    static const char *XML_BUG = "bug";
    static const char *XML_REF = "ref";
    static const char *XML_UPDATED = "updated";
    static const char *XML_DESCRIPTION = "description";
    static const char *XML_DATE = "date";
    static const char *XML_DATES = "dates";
    static const char *XML_OVAL_DEF_DATES = "oval-def:dates";
    static const char *XML_DEBIAN = "debian";
    static const char *XML_OVAL_REPOSITORY = "oval_repository";
    static const char *XML_OVAL_DEF_OV_REPO = "oval-def:oval_repository";

    for (i = 0; node[i]; i++) {
        chld_node = NULL;
        if (!node[i]->element) {
            mterror(WM_VULNDETECTOR_LOGTAG, XML_ELEMNULL);
            return OS_INVALID;
        }

        if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_DPKG_LINUX_INFO_STATE)) ||
            (dist == FEED_DEBIAN && !strcmp(node[i]->element, XML_DPKG_INFO_STATE))) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_ID)) {
                    info_state *infos;
                    os_calloc(1, sizeof(info_state), infos);
                    os_strdup(node[i]->values[j], infos->id);
                    infos->operation = infos->operation_value = NULL;
                    infos->prev = parsed_oval->info_states;
                    parsed_oval->info_states = infos;
                    if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                        goto end;
                    }
                }
            }
        } else if (!strcmp(node[i]->element, XML_CONST_VAR)) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            if (node[i]->attributes && node[i]->values) {
                variables *vars = NULL;

                for (j = 0; node[i]->attributes[j] && node[i]->values[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_ID)) {
                        os_calloc(1, sizeof(variables), vars);
                        os_strdup(node[i]->values[j], vars->id);
                        vars->prev = parsed_oval->vars;
                        parsed_oval->vars =vars;

                        for (j = 0; chld_node[j] && chld_node[j]->element &&
                                    !strcmp(chld_node[j]->element, XML_VALUE) &&
                                    chld_node[j]->content; j++) {
                            os_realloc(vars->values, (j + 2) * sizeof(char *), vars->values);
                            os_strdup(chld_node[j]->content, vars->values[j]);
                            vars->values[j + 1] = NULL;
                        }

                        vars->elements = j;
                        break;
                    }
                }
            }
        } else if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_DPKG_LINUX_INFO_TEST)) ||
                   (dist == FEED_DEBIAN && !strcmp(node[i]->element, XML_DPKG_INFO_TEST))) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            info_test *infot;
            os_calloc(1, sizeof(info_test), infot);
            infot->state = NULL;
            infot->prev = parsed_oval->info_tests;
            parsed_oval->info_tests = infot;

            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_ID)) {
                    os_strdup(node[i]->values[j], parsed_oval->info_tests->id);
                }
            }
            if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_PACKG) == OS_INVALID) {
                goto end;
            }
        } else if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_DPKG_LINUX_INFO_OBJ)) ||
                   (dist == FEED_DEBIAN && !strcmp(node[i]->element, XML_DPKG_LINUX_INFO_DEB_OBJ))) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }

            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_ID)) {
                    info_obj *info_o;
                    os_calloc(1, sizeof(info_obj), info_o);
                    os_strdup(node[i]->values[j], info_o->id);
                    info_o->prev = parsed_oval->info_objs;
                    parsed_oval->info_objs = info_o;
                    if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_OBJ) == OS_INVALID) {
                        goto end;
                    }
                }
            }
            if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_PACKG) == OS_INVALID) {
                goto end;
            }
        } else if (condition == VU_OBJ && ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_NAME)) ||
                   (dist == FEED_DEBIAN && !strcmp(node[i]->element, XML_LINUX_DEB_NAME)))) {
            if (node[i]->content && *node[i]->content) {
                w_strdup(node[i]->content, parsed_oval->info_objs->obj);
            } else {
                if (node[i]->attributes && node[i]->values) {
                    int j;
                    char *var_check = NULL;
                    char *var_ref = NULL;

                    for (j = 0; node[i]->attributes[j] && node[i]->values[j]; j++) {
                        if (!strcmp(node[i]->attributes[j], XML_VAR_REF)) {
                            if (!var_ref) {
                                os_strdup(node[i]->values[j], var_ref);
                            }
                        } else if (!strcmp(node[i]->attributes[j], XML_VAR_CHECK)) {
                            if (!var_check) {
                                os_strdup(node[i]->values[j], var_check);
                            }
                        }
                    }

                    if (!var_check || !var_ref) {
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OVAL_OBJ_INV, "Parameters 'var_check' and 'var_ref' were expected");
                    } else {
                        if (!strcmp(var_check, "at least one")) {
                            parsed_oval->info_objs->need_vars = 1;
                            parsed_oval->info_objs->obj = var_ref;
                            var_ref = NULL;
                        } else {
                            char error_msg[OS_SIZE_128];
                            snprintf(error_msg, OS_SIZE_128, "Unexpected var_check: '%s'", var_check);
                            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OVAL_OBJ_INV, error_msg);
                        }
                    }
                    free(var_ref);
                    free(var_check);
                } else {
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OVAL_OBJ_INV, "Empty object");
                }
            }
        } else if ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_DEF_EVR)) ||
                   (dist == FEED_DEBIAN && !strcmp(node[i]->element, XML_EVR))) {
            if (node[i]->attributes && *node[i]->attributes) {
                for (j = 0; node[i]->attributes[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_OPERATION)) {
                        os_strdup(node[i]->values[j], parsed_oval->info_states->operation);
                        os_strdup(node[i]->content, parsed_oval->info_states->operation_value);
                    }
                }
                if (!parsed_oval->info_states->operation && !strcmp(*node[i]->attributes, XML_DATATYPE) && !strcmp(*node[i]->values, "version")) {
                    os_strdup(vu_package_comp[VU_COMP_EQ], parsed_oval->info_states->operation);
                    os_strdup(node[i]->content, parsed_oval->info_states->operation_value);
                }

            }
        } else if ((condition == VU_PACKG) &&
                   ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_STATE)) ||
                   (dist == FEED_DEBIAN && !strcmp(node[i]->element, XML_STATE)))) {
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_STATE_REF)) {
                    if (!parsed_oval->info_tests->state) {
                        os_strdup(node[i]->values[j], parsed_oval->info_tests->state);
                    }
                }
            }
        } else if ((condition == VU_PACKG) &&
                   ((dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_LINUX_OBJ)) ||
                   (dist == FEED_DEBIAN && !strcmp(node[i]->element, XML_LINUX_DEB_OBJ)))) {
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_OBJECT_REF)) {
                    if (!parsed_oval->info_tests->obj) {
                        os_strdup(node[i]->values[j], parsed_oval->info_tests->obj);
                    }
                }
            }
        } else if (!strcmp(node[i]->element, XML_DEFINITION)) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            }
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_CLASS) &&
                    !strcmp(node[i]->values[j], XML_VULNERABILITY)) {
                    vulnerability *vuln;
                    info_cve *cves;
                    os_calloc(1, sizeof(vulnerability), vuln);
                    os_calloc(1, sizeof(info_cve), cves);

                    vuln->cve_id = NULL;
                    vuln->state_id = NULL;
                    vuln->ignore = 0;
                    vuln->package_name = NULL;
                    vuln->prev = parsed_oval->vulnerabilities;
                    cves->cveid = NULL;
                    cves->title = NULL;
                    cves->severity = NULL;
                    cves->published = NULL;
                    cves->updated = NULL;
                    cves->refs = NULL;
                    cves->flags = 0;
                    cves->prev = parsed_oval->info_cves;

                    parsed_oval->vulnerabilities = vuln;
                    parsed_oval->info_cves = cves;

                    if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                        retval = OS_INVALID;
                        goto end;
                    }
                }
            }
        } else if (!strcmp(node[i]->element, XML_REFERENCE)) {
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_REF_URL)) {
                    if (!parsed_oval->info_cves->refs)
                        os_calloc(1, sizeof(references), parsed_oval->info_cves->refs);

                    references *refs = parsed_oval->info_cves->refs;
                    os_realloc(refs->values, (refs->elements + 2) * sizeof(char *), refs->values);
                    os_strdup(node[i]->values[j], refs->values[refs->elements]);
                    refs->values[++refs->elements] = NULL;
                } else if (!strcmp(node[i]->attributes[j], XML_REF_ID)){
                    if (!parsed_oval->info_cves->cveid) {
                        os_strdup(node[i]->values[j], parsed_oval->info_cves->cveid);
                    }
                    if (!parsed_oval->vulnerabilities->cve_id) {
                        os_strdup(node[i]->values[j], parsed_oval->vulnerabilities->cve_id);
                    }
                }
            }
        } else if (!strcmp(node[i]->element, XML_TITLE)) {
                os_strdup(node[i]->content, parsed_oval->info_cves->title);
                // Debian Wheezy OVAL has its CVE of the title
                if (dist == FEED_DEBIAN && !strcmp(parsed_oval->OS, vu_feed_tag[FEED_WHEEZY])) {
                    if (!parsed_oval->info_cves->cveid) {
                        os_strdup(node[i]->content, parsed_oval->info_cves->cveid);
                    }
                    if (!parsed_oval->vulnerabilities->cve_id) {
                        os_strdup(node[i]->content, parsed_oval->vulnerabilities->cve_id);
                    }
                }
        } else if (!strcmp(node[i]->element, XML_CRITERIA)) {
            if (!node[i]->attributes) {
                if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                    goto invalid_elem;
                }
                if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                    retval = OS_INVALID;
                    goto end;
                }
            } else {
                char operator_found = 0;
                for (j = 0; node[i]->attributes[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_OPERATOR)) {
                        int result = VU_TRUE;
                        operator_found = 1;
                        if (!strcmp(node[i]->values[j], XML_OR)) {
                            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                                continue;
                            } else if (result = wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_OR), result == OS_INVALID) {
                                retval = OS_INVALID;
                                goto end;
                            }

                        } else if (!strcmp(node[i]->values[j], XML_AND)) {
                            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                                continue;
                            } else if (result = wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_AND), result == OS_INVALID) {
                                retval = OS_INVALID;
                                goto end;
                            }

                        } else {
                            mterror(WM_VULNDETECTOR_LOGTAG, VU_INVALID_OPERATOR, node[i]->values[j]);
                            retval = OS_INVALID;
                            goto end;
                        }
                    }
                }
                // Checks for version comparasions without operators
                if (!operator_found && node[i]->attributes && node[i]->values &&
                    *node[i]->attributes && *node[i]->values &&
                    !strcmp(*node[i]->attributes, XML_COMMENT) &&
                    !strcmp(*node[i]->values, "file version")) {
                    if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                        continue;
                    } else if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, VU_AND) == OS_INVALID) {
                        retval = OS_INVALID;
                        goto end;
                    }
                }
            }
        } else if (!strcmp(node[i]->element, XML_CRITERION)) {
            for (j = 0; node[i]->attributes[j]; j++) {
                if (!strcmp(node[i]->attributes[j], XML_TEST_REF)) {
                    if (parsed_oval->vulnerabilities->state_id) {
                        os_calloc(1, sizeof(vulnerability), vuln);
                        os_strdup(parsed_oval->vulnerabilities->cve_id, vuln->cve_id);
                        vuln->prev = parsed_oval->vulnerabilities;
                        vuln->state_id = NULL;
                        vuln->ignore = 0;
                        vuln->package_name = NULL;
                        parsed_oval->vulnerabilities = vuln;
                        os_strdup(node[i]->values[j], vuln->state_id);
                    } else {
                        os_strdup(node[i]->values[j], parsed_oval->vulnerabilities->state_id);
                    }
                }
                // Checks if the package isn't vulnerable
                else if (!strcmp(node[i]->attributes[j], XML_COMMENT)) {
                    STATIC const char not_vulnerable[] = "while related to the CVE in some way, a decision has been made to ignore this issue";
                    STATIC const char needs_triage[] = "is affected and may need fixing";
                    STATIC const char deferred[] = "is affected, but a decision has been made to defer addressing it";

                    // Just for Ubuntu, where comment comes after test_ref
                    if (parsed_oval->vulnerabilities->state_id) {
                        if (strstr(node[i]->values[j], not_vulnerable)) {
                            parsed_oval->vulnerabilities->ignore = 2;
                        } else if (strstr(node[i]->values[j], needs_triage) || strstr(node[i]->values[j], deferred)) {
                            parsed_oval->vulnerabilities->ignore = 1;
                        } else {
                            parsed_oval->vulnerabilities->ignore = 0;
                        }
                    }
                }
            }
        } else if (!strcmp(node[i]->element, XML_DESCRIPTION)) {
            os_strdup(node[i]->content, parsed_oval->info_cves->description);
        } else if (!strcmp(node[i]->element, XML_OVAL_PRODUCT_VERSION)) {
            os_strdup(node[i]->content, parsed_oval->metadata.product_version);
        } else if (!strcmp(node[i]->element, XML_OVAL_PRODUCT_NAME)) {
            os_strdup(node[i]->content, parsed_oval->metadata.product_name);
        } else if (!strcmp(node[i]->element, XML_DATE)) {
            os_strdup(node[i]->content, parsed_oval->info_cves->published);
        } else if (!strcmp(node[i]->element, XML_OVAL_TIMESTAMP)) {
            os_strdup(node[i]->content, parsed_oval->metadata.timestamp);
        } else if (!strcmp(node[i]->element, XML_OVAL_SCHEMA_VERSION)) {
            os_strdup(node[i]->content, parsed_oval->metadata.schema_version);
        } else if (!strcmp(node[i]->element, XML_SEVERITY)) {
            if (*node[i]->content != '\0') {
                os_strdup(node[i]->content, parsed_oval->info_cves->severity);
            } else {
                parsed_oval->info_cves->severity = NULL;
            }
        } else if (!strcmp(node[i]->element, XML_UPDATED)) {
            if (node[i]->attributes) {
                for (j = 0; node[i]->attributes[j]; j++) {
                    if (!strcmp(node[i]->attributes[j], XML_DATE)) {
                        os_strdup(node[i]->values[j], parsed_oval->info_cves->updated);
                    }
                }
            }
        } else if (dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_PUBLIC_DATE)) {
            os_strdup(node[i]->content, parsed_oval->info_cves->published);
        } else if (dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_BUG)) {
            if (!parsed_oval->info_cves->bugzilla_references)
                os_calloc(1, sizeof(references), parsed_oval->info_cves->bugzilla_references);

            references *bugrefs = parsed_oval->info_cves->bugzilla_references;
            os_realloc(bugrefs->values, (bugrefs->elements + 2) * sizeof(char *), bugrefs->values);
            os_strdup(node[i]->content, bugrefs->values[bugrefs->elements]);
            bugrefs->values[++bugrefs->elements] = NULL;
        } else if (dist == FEED_UBUNTU && !strcmp(node[i]->element, XML_REF)) {
            if (!parsed_oval->info_cves->refs)
                os_calloc(1, sizeof(references), parsed_oval->info_cves->refs);

            references *refs = parsed_oval->info_cves->refs;
            os_realloc(refs->values, (refs->elements + 2) * sizeof(char *), refs->values);
            os_strdup(node[i]->content, refs->values[refs->elements]);
            refs->values[++refs->elements] = NULL;
        } else if (!strcmp(node[i]->element, XML_OVAL_DEFINITIONS)  ||
                   !strcmp(node[i]->element, XML_DEFINITIONS)       ||
                   !strcmp(node[i]->element, XML_OBJECTS)           ||
                   !strcmp(node[i]->element, XML_VARIABLES)         ||
                   !strcmp(node[i]->element, XML_METADATA)          ||
                   !strcmp(node[i]->element, XML_OVAL_DEF_METADATA) ||
                   !strcmp(node[i]->element, XML_TESTS)             ||
                   !strcmp(node[i]->element, XML_STATES)            ||
                   !strcmp(node[i]->element, XML_ADVISORY)          ||
                   !strcmp(node[i]->element, XML_DEBIAN)            ||
                   !strcmp(node[i]->element, XML_GENERATOR)         ||
                   !strcmp(node[i]->element, XML_OVAL_REPOSITORY)   ||
                   !strcmp(node[i]->element, XML_OVAL_DEF_OV_REPO)  ||
                   !strcmp(node[i]->element, XML_DATES)             ||
                   !strcmp(node[i]->element, XML_OVAL_DEF_DATES)) {
            if (chld_node = OS_GetElementsbyNode(xml, node[i]), !chld_node) {
                goto invalid_elem;
            } else if (wm_vuldet_oval_xml_parser(xml, chld_node, parsed_oval, update, condition) == OS_INVALID) {
                retval = OS_INVALID;
                goto end;
            }
        }

        OS_ClearNode(chld_node);
        chld_node = NULL;
    }


end:
    OS_ClearNode(chld_node);
    return retval;

invalid_elem:
    mterror(WM_VULNDETECTOR_LOGTAG, XML_INVELEM, node[i]->element);
    return OS_INVALID;
}

int wm_vuldet_oval_process(update_node *update, char *path, wm_vuldet_db *parsed_vulnerabilities) {
    int success = 0;
    char *tmp_file;
    OS_XML xml;
    XML_NODE node = NULL;
    XML_NODE chld_node = NULL;

    memset(&xml, 0, sizeof(xml));

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_PRE, vu_feed_tag[update->dist_tag_ref]);
    if (tmp_file = wm_vuldet_oval_xml_preparser(path, update->dist_ref), !tmp_file) {
        goto free_mem;
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_PAR, vu_feed_tag[update->dist_tag_ref]);
    if (OS_ReadXML(tmp_file, &xml) < 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_LOAD_CVE_ERROR, vu_feed_tag[update->dist_tag_ref], xml.err);
        goto free_mem;
    }

    if (node = OS_GetElementsbyNode(&xml, NULL), !node) {
        goto free_mem;
    };

    // Reduces a level of recurrence
    if (chld_node = OS_GetElementsbyNode(&xml, *node), !chld_node) {
        goto free_mem;
    }

    if (wm_vuldet_oval_xml_parser(&xml, chld_node, parsed_vulnerabilities, update, 0) == OS_INVALID) {
        goto free_mem;
    }

    success = 1;
free_mem:
    os_free(tmp_file);
    OS_ClearNode(node);
    OS_ClearNode(chld_node);
    OS_ClearXML(&xml);
    return !success;
}

int wm_vuldet_index_feed(update_node *update) {
    wm_vuldet_db parsed_vulnerabilities;
    const char *OS_VERSION;
    char *path;
    char success = 0;

    memset(&parsed_vulnerabilities, 0, sizeof(wm_vuldet_db));
    OS_VERSION = vu_feed_tag[update->dist_tag_ref];
    parsed_vulnerabilities.OS = OS_VERSION;

    if (update->json_format) {
        int result;
        // It is a feed in JSON format
        if (result = wm_vuldet_index_json(&parsed_vulnerabilities, update,
                                update->multi_path ? update->multi_path : VU_FIT_TEMP_FILE,
                                update->multi_path ? 1 : 0), result == OS_INVALID) {
            goto free_mem;
        } else if (result == VU_NOT_NEED_UPDATE) {
            success = 1;
            goto free_mem;
        }
    } else {
        path = update->path ? update->path : VU_TEMP_FILE;

        if (update->dist_ref == FEED_UBUNTU || update->dist_ref == FEED_DEBIAN) {
            if (wm_vuldet_oval_process(update, path, &parsed_vulnerabilities)) {
                goto free_mem;
            }
        } else if (update->dist_ref == FEED_CPED) {
            if (wm_vuldet_nvd_cpe_parser(path, &parsed_vulnerabilities)) {
                goto free_mem;
            }
        }
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_START_REFRESH_DB, update->dist_ext);

    if (wm_vuldet_insert(&parsed_vulnerabilities, update)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_REFRESH_DB_ERROR, OS_VERSION);
        goto free_mem;
    }
    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_STOP_REFRESH_DB, update->dist_ext);

    success = 1;
free_mem:
    if (remove(VU_TEMP_FILE) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_FILE, strerror(errno));
    }
    if (remove(VU_FIT_TEMP_FILE) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_FIT_TEMP_FILE, strerror(errno));
    }
    if (remove(VU_TEMP_FILE_BZ2) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_FILE_BZ2, strerror(errno));
    }

    if (success) {
        return 0;
    }

    return OS_INVALID;
}

int wm_vuldet_fetch_oval(update_node *update, char *repo) {
    STATIC const char *timestamp_tag = "timestamp>";
    char timestamp[OS_SIZE_256 + 1] = {0};
    char buffer[OS_MAXSTR + 1] = {0};
    FILE *fp = NULL;
    char *found;
    int attempts;
    vu_logic retval = VU_INV_FEED;

    for (attempts = 0;; attempts++) {
        int res_url_request;
        if (update->dist_ref == FEED_UBUNTU) {
            res_url_request = wurl_request(repo, VU_TEMP_FILE_BZ2, NULL, NULL, update->timeout);
        } else {
            res_url_request = wurl_request(repo, VU_TEMP_FILE, NULL, NULL, update->timeout);
        }

        if (!res_url_request) {
            break;
        } else if (attempts == WM_VULNDETECTOR_DOWN_ATTEMPTS) {
            goto end;
        }
        mdebug1(VU_DOWNLOAD_FAIL, attempts);
        sleep(attempts);
    }

    if (update->dist_ref == FEED_UBUNTU) {
        int result;
        if (result = bzip2_uncompress(VU_TEMP_FILE_BZ2, VU_TEMP_FILE), result == -1) {
            goto end;
        }
    }

    if (fp = fopen(VU_TEMP_FILE, "r"), !fp) {
        goto end;
    }

    while (fgets(buffer, OS_MAXSTR, fp)) {
        if (found = strstr(buffer, timestamp_tag), found) {
            char *close_tag;
            found+=strlen(timestamp_tag);

            if (close_tag = strstr(found, "<"), !close_tag) {
                goto end;
            }
            *close_tag = '\0';

            switch (wm_vuldet_check_timestamp(vu_feed_tag[update->dist_tag_ref], found, timestamp)) {
                case VU_TIMESTAMP_FAIL:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED_ERROR, update->dist_ext);
                    goto end;
                break;
                case VU_TIMESTAMP_UPDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, update->dist_ext, timestamp);
                    retval = VU_NOT_NEED_UPDATE;
                    goto end;
                break;
                case VU_TIMESTAMP_OUTDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED, update->dist_ext, "");
                break;
            }
            break;
        }
    }

    retval = VU_NEED_UPDATE;
end:
    if (fp) {
        w_fclose(fp);
    }

    return retval;
}

int wm_vuldet_fetch_redhat(update_node *update) {
    int attempt = 1;
    int retval = VU_TRY_NEXT_PAGE;
    FILE *fp = NULL;
    char *repo = NULL;
    char buffer[OS_SIZE_128 + 1];

    if (update->multi_url) {
        char tag[10 + 1];

        snprintf(tag, 10, "%d", update->update_it);
        repo = wstr_replace(update->multi_url, MULTI_URL_TAG, tag);
    } else {
        os_calloc(OS_SIZE_2048 + 1, sizeof(char), repo);
        snprintf(repo, OS_SIZE_2048, RED_HAT_REPO, update->update_from_year, RED_HAT_REPO_REQ_SIZE, update->update_it);
    }

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DOWNLOAD_START, repo);
    while (1) {
        if (wurl_request(repo, VU_FIT_TEMP_FILE, NULL, NULL, update->timeout)) {
            if (attempt == RED_HAT_REPO_MAX_ATTEMPTS) {
                mtwarn(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV_NEW, repo, RED_HAT_REPO_MAX_ATTEMPTS);
                update->update_state = VU_TRY_NEXT_PAGE;
                retval = 0;
                goto end;
            }

            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, attempt);
            attempt++;
            sleep(attempt * DOWNLOAD_SLEEP_FACTOR);
        } else {
            break;
        }
    }

    if (fp = fopen(VU_FIT_TEMP_FILE, "r"), !fp) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "fopen(%s): %s", VU_FIT_TEMP_FILE, strerror(errno));
        retval = OS_INVALID;
        update->update_state = VU_INV_FEED;
        goto end;
    }

    if (!fgets(buffer, OS_SIZE_128, fp) || !strncmp(buffer, "[]", 2)) {
        if (remove(VU_FIT_TEMP_FILE) < 0) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_FIT_TEMP_FILE, strerror(errno));
        }
        update->update_state = VU_FINISH_FETCH;
    } else {
        update->update_state = VU_SUCC_DOWN_PAGE;
    }

    retval = 0;
end:
    free(repo);
    if (fp) {
        fclose(fp);
    }
    return retval;
}

const char *wm_vuldet_decode_package_version(char *raw, const char **OS, char **OS_minor, char **package_name, char **package_version) {
    STATIC OSRegex *reg = NULL;
    STATIC char *package_regex = "(-\\d+:v\\d+)|(-\\d+:\\d+)|(:v\\d+)|(:\\d+)|(-\\d+-\\d+)|(-\\d+.\\d+-\\d+)|(-\\d+.\\d+.\\d+-\\d+)|(-\\d+.\\d+.\\d+.)|(-\\d+-)|(-\\d+.)";
    const char *retv = NULL;
    const char *ext = NULL;
    char *found;
    char *aux;
    size_t len;

    if (!reg) {
        os_calloc(1, sizeof(OSRegex), reg);
        if(OSRegex_Compile(package_regex, reg, OS_RETURN_SUBSTRING) == 0) {
            OSRegex_FreePattern(reg);
            os_free(reg);
            return NULL;
        }
    }

    if (retv = OSRegex_Execute(raw, reg), retv) {
        if(*reg->d_sub_strings == NULL){
            OSRegex_FreePattern(reg);
            os_free(reg);
            return NULL;
        }
        if (found = strstr(raw, *reg->d_sub_strings), !found) {
            OSRegex_FreePattern(reg);
            os_free(reg);
            return NULL;
        }
        // Remove 'v' from version
        aux = found;
        if (aux = strchr(aux, 'v'), aux) {
            if (*(aux - 1) == ':') {
                memmove(aux, aux + 1, strlen(aux + 1) + 1);
            }
        }
        *found = '\0';
        w_strdup(found + 1, *package_version);
        w_strdup(raw, *package_name);

        // We will only keep standard RHEL packages (without tags)
        if (strstr(*package_version, ".atomic") ||
            strstr(*package_version, ".ep") ||
            strstr(*package_version, ".jbcs") ||
            strstr(*package_version, ".openstack") ||
            strstr(*package_version, ".rhaos") ||
            strstr(*package_version, ".rt")) {
                OSRegex_FreePattern(reg);
                os_free(reg);
                return retv;
        }

        if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_8]), found) {
            *OS = vu_feed_tag[FEED_RHEL8];
            ext = vu_package_dist[VU_RH_EXT_8];
        } else if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_7]), found) {
            *OS = vu_feed_tag[FEED_RHEL7];
            ext = vu_package_dist[VU_RH_EXT_7];
        } else if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_6]), found) {
            *OS = vu_feed_tag[FEED_RHEL6];
            ext = vu_package_dist[VU_RH_EXT_6];
        } else if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_5]), found) {
            *OS = vu_feed_tag[FEED_RHEL5];
            ext = vu_package_dist[VU_RH_EXT_5];
        }

        if (!(*OS)) {
            // In some cases, RHEL reports packages with .aelXb_Y extension
            if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_8_A]), found) {
                *OS = vu_feed_tag[FEED_RHEL8];
                ext = vu_package_dist[VU_RH_EXT_8];
            } else if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_7_A]), found) {
                *OS = vu_feed_tag[FEED_RHEL7];
                ext = vu_package_dist[VU_RH_EXT_7];
            } else if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_6_A]), found) {
                *OS = vu_feed_tag[FEED_RHEL6];
                ext = vu_package_dist[VU_RH_EXT_6];
            } else if (found = strstr(*package_version, vu_package_dist[VU_RH_EXT_5_A]), found) {
                *OS = vu_feed_tag[FEED_RHEL5];
                ext = vu_package_dist[VU_RH_EXT_5];
            }
            if (*OS) {
                // Convert extension from .aelXb_Y to .elX_Y
                aux = found;
                if (aux = strstr(aux, ".ael"), aux) {
                    memmove(aux + 1, aux + 2, strlen(aux + 2) + 1);
                    if (aux = strchr(aux, 'b'), aux) {
                        if ((*(aux + 1) == '\0') || (*(aux + 1) == '_') || (*(aux + 1) == '.')) {
                            memmove(aux, aux + 1, strlen(aux + 1) + 1);
                        }
                    }
                }
            }
        }

        if (*OS && found && ext) {
            // We will only keep standard RHEL packages (without tags)
            len = strlen(ext);
            if ((*(found + len) != '\0') && (*(found + len) != '_') && (*(found + len) != '.')) {
                *OS = NULL;
            } else {
                wm_vuldet_set_subversion(found, OS_minor);

                if (*OS_minor) {
                    len += strlen(*OS_minor) + 1;
                    if ((*(found + len) != '\0') && (*(found + len) != '.')) {
                        os_free(*OS_minor);
                        *OS = NULL;
                        *OS_minor = NULL;
                    }
                }
            }
        }
    }

    OSRegex_FreePattern(reg);
    os_free(reg);
    return retv;
}

int wm_vuldet_check_timestamp(const char *target, char *timst, char *ret_timst) {
    int retval = VU_TIMESTAMP_FAIL;
    const char *stored_timestamp;
    sqlite3_stmt *stmt = NULL;
    sqlite3 *db = NULL;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
        goto end;
    } else {
        if (wm_vuldet_prepare(db, vu_queries[TIMESTAMP_QUERY], -1, &stmt, NULL) != SQLITE_OK) {
            goto end;
        }
        sqlite3_bind_text(stmt, 1, target, -1, NULL);
        if (wm_vuldet_step(stmt) == SQLITE_ROW) {
            if (stored_timestamp = (const char *) sqlite3_column_text(stmt, 0), stored_timestamp) {
                if (!strcmp(stored_timestamp, timst)) {
                    retval = VU_TIMESTAMP_UPDATED;
                    if (ret_timst) {
                        snprintf(ret_timst, OS_SIZE_256, "%s", stored_timestamp);
                    }
                    goto end;
                }
            }
        }
        retval = VU_TIMESTAMP_OUTDATED;
    }
end:
    if (db) {
        sqlite3_close_v2(db);
    }
    wdb_finalize(stmt);
    return retval;
}

int wm_vuldet_fetch_feed(update_node *update, int *need_update) {
    char repo[OS_SIZE_2048 + 1] = { '\0' };
    char *low_repo;
    unsigned char success = 0;
    int result;
    *need_update = 1;

    if (!update->url && (update->path || update->multi_path)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_LOCAL_FETCH, update->path ? update->path : update->multi_path);
        return 0;
    }

    if (!update->url) {
        // Ubuntu and debian build their repos in a specific way
        if (update->dist_ref == FEED_UBUNTU) {
            os_strdup(update->version, low_repo);
            str_lowercase(low_repo);
            snprintf(repo, OS_SIZE_2048, CANONICAL_REPO, low_repo);
            free(low_repo);
        } else if (update->dist_ref == FEED_DEBIAN) {
            os_strdup(update->version, low_repo);
            str_lowercase(low_repo);
            snprintf(repo, OS_SIZE_2048, DEBIAN_REPO, low_repo);
            free(low_repo);
        } else if (update->dist_ref != FEED_REDHAT &&
                   update->dist_ref != FEED_CPED &&
                   update->dist_ref != FEED_NVD &&
                   update->dist_ref != FEED_CPEW &&
                   update->dist_ref != FEED_MSU) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_OS_VERSION_ERROR);
            return OS_INVALID;
        }
    } else {
        snprintf(repo, OS_SIZE_2048, "%s", update->url);
    }

    // Call to the specific fetch function
    switch (update->dist_ref) {
        case FEED_REDHAT:
            update->update_state = 0;
            if (result = wm_vuldet_fetch_redhat(update), result != OS_INVALID) {
                success = 1;
                if (update->update_state == VU_FINISH_FETCH || update->update_state == VU_TRY_NEXT_PAGE || update->update_state == VU_INV_FEED) {
                    *need_update = 0;
                }
            }

            goto end;
        break;
        case FEED_CPED:
            if (wm_vuldet_fetch_nvd_cpe(update->timeout, repo)) {
                goto end;
            }
        break;
        case FEED_NVD:
            if (result = wm_vuldet_fetch_nvd_cve(update), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
        break;
        case FEED_CPEW:
            if (result = wm_vuldet_fetch_wazuh_cpe(update), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
        break;
        case FEED_MSU:
            if (result = wm_vuldet_fetch_MSU(), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
        break;
        default:
            if (result = wm_vuldet_fetch_oval(update, repo), result == VU_INV_FEED) {
                goto end;
            } else if (result == VU_NOT_NEED_UPDATE) {
                *need_update = 0;
            }
    }

    success = 1;
end:
    if (success) {
        return 0;
    } else {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_FETCH_ERROR, update->dist_ext);
        return OS_INVALID;
    }
}

int wm_vuldet_check_feed(update_node *upd, int *error_code) {
    if (wm_vuldet_check_update_period(upd)) {
        if (!wm_vuldet_silent_feed(upd->dist_tag_ref)) {
            mtinfo(WM_VULNDETECTOR_LOGTAG, VU_STARTING_UPDATE, vu_feed_ext[upd->dist_tag_ref]);
        } else {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_STARTING_UPDATE, vu_feed_ext[upd->dist_tag_ref]);
        }
        if (wm_vuldet_update_feed(upd)) {
            if (!upd->attempted) {
                upd->last_update = time(NULL) - upd->interval + WM_VULNDETECTOR_RETRY_UPDATE;
                upd->attempted = 1;
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_RETRY, upd->dist, upd->version ? upd->version : "provider", (long unsigned)WM_VULNDETECTOR_RETRY_UPDATE);
            } else {
                upd->last_update = time(NULL);
                upd->attempted = 0;
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_RETRY, upd->dist, upd->version ? upd->version : "provider", upd->interval);
            }

            if (upd->dist_tag_ref == FEED_CPEW || upd->dist_tag_ref == FEED_MSU) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NVD_UPD_CANCEL,
                        upd->dist_tag_ref == FEED_CPEW ? "Wazuh CPE Helper" : "MSU feed");
                *error_code = 1;
            }

            return OS_INVALID;
        } else {
            if (!wm_vuldet_silent_feed(upd->dist_tag_ref)) {
                mtinfo(WM_VULNDETECTOR_LOGTAG, VU_ENDING_UPDATE, upd->dist_ext);
            } else {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_STARTING_UPDATE, vu_feed_ext[upd->dist_tag_ref]);
            }
            upd->last_update = time(NULL);
        }
    }
    return 0;
}

cJSON *wm_vuldet_get_debian_status_feed(const long timeout) {
    FILE *deb_file = NULL;
    int attempts;
    int res_url_request;

    if (deb_file = fopen(VU_DEB_TEMP_FILE, "r"), !deb_file) {
        // Download Debian status feed
        for (attempts = 0;; attempts++) {
            if (res_url_request = wurl_request(DEBIAN_REPO_STATUS, VU_DEB_TEMP_FILE, NULL, NULL, timeout), !res_url_request) {
                break;
            } else if (attempts == WM_VULNDETECTOR_DOWN_ATTEMPTS) {
                return NULL;
            }
            mdebug1(VU_DOWNLOAD_FAIL, attempts);
            sleep(attempts);
        }
    } else {
        fclose(deb_file);
    }

    // Parse Debian status feed
    return wm_vuldet_json_fread(VU_DEB_TEMP_FILE);
}

int wm_vuldet_run_update(update_node **updates) {
    int error_code = 0;
    int ret = 0;

        // Ubuntu
    if (wm_vuldet_check_feed(updates[CVE_FOCAL], &error_code)    ||
        wm_vuldet_check_feed(updates[CVE_BIONIC], &error_code)   ||
        wm_vuldet_check_feed(updates[CVE_XENIAL], &error_code)   ||
        wm_vuldet_check_feed(updates[CVE_TRUSTY], &error_code)   ||
        wm_vuldet_check_feed(updates[CVE_PRECISE], &error_code)  ||
        // Debian
        wm_vuldet_check_feed(updates[CVE_BUSTER], &error_code)   ||
        wm_vuldet_check_feed(updates[CVE_STRETCH], &error_code)  ||
        wm_vuldet_check_feed(updates[CVE_JESSIE], &error_code)   ||
        wm_vuldet_check_feed(updates[CVE_WHEEZY], &error_code)   ||
        // RedHat
        wm_vuldet_check_feed(updates[CVE_REDHAT], &error_code)   ||
        // Wazuh CPE feed
        wm_vuldet_check_feed(updates[CPE_WDIC], &error_code)     ||
        // Microsoft Security Update
        wm_vuldet_check_feed(updates[CVE_MSU], &error_code)      ||
        // NVD
        wm_vuldet_check_feed(updates[CVE_NVD], &error_code)) {

        if (error_code == 1) {
            // The CPE helper update failed
            wm_vuldet_release_update_node(updates, CPE_WDIC);
            wm_vuldet_release_update_node(updates, CVE_MSU);
            wm_vuldet_release_update_node(updates, CVE_NVD);
        }

        ret = OS_INVALID;
    }

    // Remove Debian status feed
    if (remove(VU_DEB_TEMP_FILE) < 0 && errno != ENOENT) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_DEB_TEMP_FILE, strerror(errno));
    }

    return ret;
}

int wm_vuldet_json_rh_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    cJSON *json_it;
    cJSON *cve_content;
    time_t l_time;
    struct tm *tm_time;

    static char *JSON_CVE = "CVE";
    static char *JSON_SEVERITY = "severity";
    static char *JSON_PUBLIC_DATE = "public_date";
    static char *JSON_ADVISORIES = "advisories";
    static char *JSON_BUGZILLA = "bugzilla";
    static char *JSON_BUGZILLA_DESCRIPTION = "bugzilla_description";
    static char *JSON_CVSS_SCORE = "cvss_score";
    static char *JSON_CVSS_SCORING_VECTOR = "cvss_scoring_vector";
    static char *JSON_CWE = "CWE";
    static char *JSON_AFFECTED_PACKAGES = "affected_packages";
    static char *JSON_RESOURCE_URL = "resource_url";
    static char *JSON_CVSS3_SCORE = "cvss3_score";
    static char *JSON_CVSS3_SCORING_VECTOR = "cvss3_scoring_vector";

    // Metadata values
    STATIC char *rh_product_name = "Red Hat Security Data";
    STATIC char *rh_product_version = "1.0";
    char *m_product_name = NULL;
    char *m_product_version = NULL;
    char *m_schema_version = NULL;
    char m_timestamp[27] = { '\0' };

    for (json_it  = json_feed->child; json_it; json_it = json_it->next) {
        char *tmp_cve = NULL;
        char *tmp_severity = NULL;
        char *tmp_public_date = NULL;
        cJSON *tmp_advisories = NULL;
        char *tmp_bugzilla = NULL;
        char *tmp_bugzilla_description = NULL;
        double tmp_cvss_score = -1;
        char *tmp_cvss_scoring_vector = NULL;
        char *tmp_cwe = NULL;
        cJSON *tmp_affected_packages = NULL;
        double tmp_cvss3_score = -1;
        char *tmp_cvss3_scoring_vector = NULL;

        time(&l_time);
        tm_time = localtime(&l_time);
        strftime(m_timestamp, 26, "%Y-%m-%d %H:%M:%S", tm_time);
        m_product_name = rh_product_name;
        m_product_version = rh_product_version;

        for (cve_content  = json_it->child; json_tagged_obj(cve_content); cve_content = cve_content->next) {
            if (!strcmp(cve_content->string, JSON_CVE)) {
                tmp_cve = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_SEVERITY)) {
                tmp_severity = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_PUBLIC_DATE)) {
                tmp_public_date = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_ADVISORIES)) {
                tmp_advisories = cve_content->child;
            } else if (!strcmp(cve_content->string, JSON_BUGZILLA)) {
                tmp_bugzilla = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_BUGZILLA_DESCRIPTION)) {
                tmp_bugzilla_description = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_CVSS_SCORE)) {
                if (cve_content->type == cJSON_String) {
                    tmp_cvss_score = atof(cve_content->valuestring);
                } else if (cve_content->type == cJSON_Number) {
                    tmp_cvss_score = cve_content->valuedouble;
                }
            } else if (!strcmp(cve_content->string, JSON_CVSS_SCORING_VECTOR)) {
                tmp_cvss_scoring_vector = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_CWE)) {
                tmp_cwe = cve_content->valuestring;
            } else if (!strcmp(cve_content->string, JSON_AFFECTED_PACKAGES)) {
                tmp_affected_packages = cve_content->child;
            } else if (!strcmp(cve_content->string, JSON_RESOURCE_URL)) {
            } else if (!strcmp(cve_content->string, JSON_CVSS3_SCORE)) {
                if (cve_content->type == cJSON_String) {
                    tmp_cvss3_score = atof(cve_content->valuestring);
                } else if (cve_content->type == cJSON_Number) {
                    tmp_cvss3_score = cve_content->valuedouble;
                }
            } else if (!strcmp(cve_content->string, JSON_CVSS3_SCORING_VECTOR)) {
                tmp_cvss3_scoring_vector = cve_content->valuestring;
            } else {
                mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_UNEXP_JSON_KEY, cve_content->string);
            }
        }

        if(!tmp_bugzilla_description || !tmp_cve) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FEED_NODE_NULL_ELM);
            return OS_INVALID;
        }

        wm_vuldet_adapt_title(tmp_bugzilla_description, tmp_cve);

        if (tmp_affected_packages) {
            // Fill in vulnerability information
            wm_vuldet_add_vulnerability_info(parsed_vulnerabilities);
            w_strdup(tmp_cve, parsed_vulnerabilities->info_cves->cveid);
            w_strdup(tmp_severity, parsed_vulnerabilities->info_cves->severity);
            w_strdup(tmp_public_date, parsed_vulnerabilities->info_cves->published);

            // Setting the reference. In RH we only have one reference link to save in the array for each CVE
            if (!parsed_vulnerabilities->info_cves->refs)
                os_calloc(1, sizeof(references), parsed_vulnerabilities->info_cves->refs);

            references *refs = parsed_vulnerabilities->info_cves->refs;
            os_realloc(refs->values, (refs->elements + 2) * sizeof(char *), refs->values);
            refs->values[refs->elements] = wm_vuldet_build_url(VU_BUILD_REF_CVE_RH, tmp_cve);
            refs->values[++refs->elements] = NULL;

            w_strdup(tmp_bugzilla_description, parsed_vulnerabilities->info_cves->description);
            parsed_vulnerabilities->info_cves->cvss = (tmp_cvss_score != -1) ? w_double_str(tmp_cvss_score) : NULL;
            parsed_vulnerabilities->info_cves->cvss3 = (tmp_cvss3_score != -1) ? w_double_str(tmp_cvss3_score) : NULL;
            w_strdup(tmp_cvss_scoring_vector, parsed_vulnerabilities->info_cves->cvss_vector);
            w_strdup(tmp_cvss3_scoring_vector, parsed_vulnerabilities->info_cves->cvss3_vector);

            // Setting the bugzilla reference. In RH we only have one bugzilla reference link to save in the array for each CVE
            if (!parsed_vulnerabilities->info_cves->bugzilla_references)
                os_calloc(1, sizeof(references), parsed_vulnerabilities->info_cves->bugzilla_references);

            references *bugrefs = parsed_vulnerabilities->info_cves->bugzilla_references;
            os_realloc(bugrefs->values, (bugrefs->elements + 2) * sizeof(char *), bugrefs->values);
            bugrefs->values[bugrefs->elements] = wm_vuldet_build_url(VU_BUILD_REF_BUGZ, tmp_bugzilla);
            bugrefs->values[++bugrefs->elements] = NULL;

            parsed_vulnerabilities->info_cves->advisories = wm_vuldet_extract_advisories(tmp_advisories);
            w_strdup(tmp_cwe, parsed_vulnerabilities->info_cves->cwe);

            rh_vulnerability *new_vuln = NULL;

            // Set the vulnerability - package relationship
            for (; tmp_affected_packages && tmp_affected_packages->valuestring; tmp_affected_packages = tmp_affected_packages->next) {
                wm_vuldet_add_rvulnerability(parsed_vulnerabilities);
                new_vuln = parsed_vulnerabilities->rh_vulnerabilities;
                w_strdup(tmp_cve, new_vuln->cve_id);

                if (!wm_vuldet_decode_package_version(tmp_affected_packages->valuestring,
                    &new_vuln->OS, &new_vuln->OS_minor, &new_vuln->package_name, &new_vuln->package_version)) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VER_EXTRACT_ERROR, tmp_affected_packages->valuestring);
                }

                if (!new_vuln->OS) {
                    // The operating system of the package could not be specified. It will be discarded
                    os_free(new_vuln->cve_id);
                    os_free(new_vuln->package_name);
                    os_free(new_vuln->package_version);
                    parsed_vulnerabilities->rh_vulnerabilities = new_vuln->prev;
                    os_free(new_vuln);
                }
            }
        }
    }

    // Insert metadata values
    os_free(parsed_vulnerabilities->metadata.product_name);
    w_strdup(m_product_name, parsed_vulnerabilities->metadata.product_name);
    os_free(parsed_vulnerabilities->metadata.product_version);
    w_strdup(m_product_version, parsed_vulnerabilities->metadata.product_version);
    os_free(parsed_vulnerabilities->metadata.schema_version);
    w_strdup(m_schema_version, parsed_vulnerabilities->metadata.schema_version);
    os_free(parsed_vulnerabilities->metadata.timestamp);
    os_strdup(m_timestamp, parsed_vulnerabilities->metadata.timestamp);

    return 0;
}

int wm_vuldet_json_parser(char *json_path, wm_vuldet_db *parsed_vulnerabilities, update_node *update) {
    int retval = OS_INVALID;

    if (update->dist_ref == FEED_NVD) {
        char *json_feed;

        if (json_feed = w_get_file_content(json_path, JSON_MAX_FSIZE), !json_feed) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_CONTENT_FEED_ERROR, update->dist_ext, json_path);
            return retval;
        }

        if (retval = wm_vuldet_json_nvd_parser(json_feed, parsed_vulnerabilities), retval == OS_INVALID) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_PARSED_FEED_ERROR, update->dist_ext, json_path);
        }
        free(json_feed);
    }
    else {
        cJSON *json_feed;

        if (json_feed = wm_vuldet_json_fread(json_path), !json_feed) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_PARSED_FEED_ERROR, update->dist_ext, json_path);
            return retval;
        }

        switch ((int) update->dist_ref) {
            case FEED_REDHAT:
                retval = wm_vuldet_json_rh_parser(json_feed, parsed_vulnerabilities);
                break;
            case FEED_CPEW:
                retval = wm_vuldet_json_wcpe_parser(json_feed, parsed_vulnerabilities);
                break;
            case FEED_MSU:
                retval = wm_vuldet_json_msu_parser(json_feed, parsed_vulnerabilities);
                break;
            default:
                break;
        }
        cJSON_Delete(json_feed);
    }

    return retval;
}

void wm_parse_rpm_source_package(agent_software *agent, const char *product, char **source, const char *version, char **src_version) {
    char *tok = NULL;
    char *src_name = NULL;
    char *saved = NULL;
    char *tmp_src = NULL;
    size_t len = 0;

    /* Discard all sources which have same name as
     their respective binary. */
    if (strstr(*source, product)) {
        goto clean;
    }

    os_strdup(*source, tmp_src);

    tok = strtok_r(tmp_src, "-", &saved);
    if (tok == NULL || saved == NULL || strlen(saved) == 0) {
        mdebug2(VU_NO_SRC_NAME, *source, atoi(agent->agent_id));
        goto clean;
    }

    // Parse and retrieve the sources's full package name.
    os_calloc(1, OS_SIZE_256 + 1, src_name);
    while (tok != NULL && len < OS_SIZE_256) {
        len = strlen(src_name);
        if (len) {
            strncat(src_name, "-", OS_SIZE_256 - len);
            len++;
        }
        strncat(src_name, tok, OS_SIZE_256 - len);
        if (isdigit(saved[0])) break; // the version starts after a '-%d'
        tok = strtok_r(NULL, "-", &saved);
    }

    // Parse the source's version.
    tok = strstr(saved, ".src.rpm");
    if (tok == NULL) {
        mdebug2(VU_NO_SRC_VERSION, *source, atoi(agent->agent_id));
        goto clean;
    }
    *tok = '\0';

    /* If the binary's version is equal to the one within the source,
    but also includes an epoch, use it as the source's version. Otherwise,
    set the source's epoch to -1. */
    len = strlen(saved);
    if (strstr(version, saved)) {
        os_strdup(version, *src_version);
    }
    else {
        os_calloc(1, len + 5, *src_version);
        snprintf(*src_version, len + 4, "-1:%s", saved);
    }

clean:
    *source = NULL;
    if (*src_version && src_name) {
        os_strdup(src_name, *source);
    }
    os_free(src_name);
    os_free(tmp_src);
}

int wm_vuldet_get_software_info(agent_software *agent, sqlite3 *db, time_t ignore_time, wm_vuldet_flags *flags) {
    unsigned int i;
    int size;
    char buffer[OS_SIZE_6144];
    char json_str[OS_SIZE_6144 + 10];
    char scan_id[OS_SIZE_128];
    int request;
    int retval = OS_INVALID;
    cJSON *obj = NULL;
    cJSON *package_list = NULL;
    int min_cpe_index;
    cpe_list *node_list = NULL;
    const char *jsonErrPtr;
    char hotfix_scan = 0;
    char package_scan = 1;
    char hotfix_config_enabled = 0;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_SOFTWARE_REQ, atoi(agent->agent_id));

    // Check to see if the scan has already been reported
    request = wm_vuldet_select_scan_type(agent->agent_id, ignore_time, &hotfix_config_enabled);
    switch (request) {
        case OS_INVALID:
            goto end;
        case VU_SOFTWARE_FULL_REQ:
            if (agent->dist == FEED_WIN && wm_vuldet_clean_agent_cpes(agent->agent_id)) {
                goto end;
            }
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AG_FULL_SCAN, atoi(agent->agent_id));
            flags->report_kernel_os_package = 1;
            wm_vuldet_update_last_scan(agent->agent_id);
            if (agent->dist == FEED_WIN) {
                hotfix_scan = 1;
            }
            break;
        case VU_SOFTWARE_REQUEST:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AG_PART_SCAN, atoi(agent->agent_id));
            // The OS and kernel inserted manually in the agent DB have to respect the ignore_time
            // But they are scanned to resolve dependencies from the NVD vulnerabilities
            flags->report_kernel_os_package = 0;
    }

    // Get the last sys_program scan ID
    switch (wm_vuldet_get_last_software_scan(agent->agent_id, scan_id)) {
        case 1: // No sys_program scan
            // We will not continue scanning unless it is a
            // Windows agent and hotfix scanning is enabled
            if (!hotfix_scan) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NO_SYSC_SCANS, atoi(agent->agent_id));
                retval = 2;
                goto end;
            } else {
                package_scan = 0;
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NO_PACKAGE_SCAN, atoi(agent->agent_id));
            }
        case 0:
            break;
        case OS_INVALID:
            goto end;
        }

    // Request and store packages
    i = 0;
    snprintf(buffer, OS_SIZE_6144, vu_queries[request], agent->agent_id, scan_id, VU_MAX_PACK_REQ, i);
    if (wm_vuldet_send_wdb(buffer)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SOFTWARE_REQUEST_ERROR, atoi(agent->agent_id));
        goto end;
    }

    while (size = wm_vuldet_recv_wdb(buffer, OS_SIZE_6144), size != OS_INVALID) {
        if (size > 0) {
            if (size < 10) {
                break;
            }
            if (wm_vuldet_wdb_valid_answ(buffer)) {
                buffer[0] = buffer[1] = ' ';
                snprintf(json_str, OS_SIZE_6144 + 10, "{\"data\":%s}", buffer);
            } else {
                goto end;
            }
            if (obj) {
                cJSON *new_obj;
                cJSON *data;
                if (new_obj = cJSON_ParseWithOpts(json_str, &jsonErrPtr, 0), !new_obj) {
                    goto end;
                } else if (!cJSON_IsObject(new_obj)) {
                    free(new_obj);
                    goto end;
                }
                data = cJSON_GetObjectItem(new_obj, "data");
                if (data) {
                    cJSON_AddItemToArray(package_list, data->child);
                    free(data->string);
                    free(data);
                }
                free(new_obj);
            } else if (obj = cJSON_ParseWithOpts(json_str, &jsonErrPtr, 0), obj && cJSON_IsObject(obj)) {
                package_list = cJSON_GetObjectItem(obj, "data");
                if (!package_list) {
                    goto end;
                }
            } else {
                goto end;
            }

            i += VU_MAX_PACK_REQ;
            snprintf(buffer, OS_SIZE_6144, vu_queries[request], agent->agent_id, scan_id, VU_MAX_PACK_REQ, i);
            if (wm_vuldet_send_wdb(buffer)) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_SOFTWARE_REQUEST_ERROR, atoi(agent->agent_id));
                goto end;
            }
        } else {
            goto end;
        }
    }

    // Avoid checking the same packages again
    snprintf(buffer, OS_SIZE_6144, vu_queries[VU_SYSC_UPDATE_SCAN], agent->agent_id, scan_id);
    if (wm_vuldet_wdb_request(buffer, OS_SIZE_6144)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_SOFTWARE_REQUEST_ERROR, atoi(agent->agent_id));
        goto end;
    }

    if (wm_vuldet_get_min_cpe_index(db, &min_cpe_index)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_CPE_INDEX_GET_ERROR);
        goto end;
    }

    if (hotfix_scan) {
        // Request the agent hotfixes
        int result;
        if (result = wm_vuldet_request_hotfixes(db, agent->agent_id), result) {
            if (result == OS_INVALID) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_HOTFIX_REQUEST_ERROR, atoi(agent->agent_id));
                goto end;
            } else if (!package_scan && !hotfix_config_enabled) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NO_HOTFIX_AVAIL, atoi(agent->agent_id));
                retval = 0;
                goto end;
            }
        }

        if (hotfix_config_enabled) {
            // Get the os info
            if (wm_vuldet_get_oswindows_info(agent)) {
                goto end;
            }

            // Generate the OS CPE
            if (wm_vuldet_generate_os_cpe(db, agent, &min_cpe_index, &node_list)) {
                goto end;
            }
            agent->info = 1;
        }
    }

    if (agent->dist != FEED_WIN) {
        // Get the os info
        if (wm_vuldet_get_oslinux_info(agent)) {
            goto end;
        }

        // Generate the OS CPE
        if (wm_vuldet_generate_os_and_kernel_package(db, agent)) {
            goto end;
        }
        agent->info = 1;
    }

    if (package_list) {
        sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);

        for (package_list = package_list->child; package_list; package_list = package_list->next) {
            char *name = (char *) wm_vuldet_get_json_value(package_list, "name");
            char *version = (char *) wm_vuldet_get_json_value(package_list, "version");
            char *architecture = (char *) wm_vuldet_get_json_value(package_list, "architecture");
            char *cpe_str = (char *) wm_vuldet_get_json_value(package_list, "cpe");
            char *vendor = (char *) wm_vuldet_get_json_value(package_list, "vendor");
            char *msu_name = (char *) wm_vuldet_get_json_value(package_list, "msu_name");
            char *source = (char *) wm_vuldet_get_json_value(package_list, "source");
            char *src_version = NULL;

            if (name && version && architecture) {
                struct cpe *s_cpe = NULL;
                char success = 1;

                if (cpe_str) {
                    s_cpe = wm_vuldet_decode_cpe(cpe_str);
                    w_strdup(msu_name, s_cpe->msu_name);
                }

                if (agent->dist != FEED_WIN) {
                    if (wm_vuldet_discard_kernel_package(agent, name, version, architecture)) {
                        continue;
                    }
                }

                if (source) {
                    if (agent->dist == FEED_REDHAT) {
                        wm_parse_rpm_source_package(agent, name, &source, version, &src_version);
                    } else {
                        // Separate version from source
                        char *source_end = strstr(source, " ");
                        char *source_version_start;
                        char *source_version_end;
                        if (source_end) {
                            source_version_start = strstr(source_end, "(");
                            if (source_version_start) {
                                source_version_end = strstr(source_version_start, ")");
                                if (source_version_end) {
                                    src_version = source_version_start + 1;
                                    *source_version_end = '\0';
                                }
                            }
                            *source_end = '\0';
                        }
                    }
                }

                // Generate the node_list structure with those packages
                // that have associated CPE
                if (wm_vuldet_insert_agent_data(db, agent, &min_cpe_index, vendor, name,
                                                source, version, src_version, architecture, s_cpe, &node_list)) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR,
                            cpe_str ? cpe_str : name);
                    success = 0;
                }

                if (agent->dist == FEED_REDHAT && src_version) {
                    os_free(source);
                    os_free(src_version);
                }

                if (s_cpe) {
                    wm_vuldet_free_cpe(s_cpe);
                }

                if (!success) {
                    goto end;
                }
            }
        }
        sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
        agent->info = 1;
    } else if (request == VU_SOFTWARE_REQUEST) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_NO_SOFTWARE, atoi(agent->agent_id));
        if (agent->dist != FEED_WIN) {
            // If no new inventory is available, skip the agent
            // Only for Linux agents, on Windows the hotfixes scan may be enabled
            retval = 2;
            goto end;
        }
    }

    // Generate the agent's CPEs
    // Only for Windows agents
    if (agent->dist == FEED_WIN) {
        sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);
        // Add the CPEs obtained from Wazuh-db
        if (node_list && wm_vuldet_insert_cpe_db(db, node_list, 0)) {
            sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
            mterror(WM_VULNDETECTOR_LOGTAG, VU_WAZUH_DB_CPE_IN_ERROR, atoi(agent->agent_id));
            goto end;
        }

        // Attempts to generate the remaining CPEs
        if (wm_vuldet_generate_agent_cpes(db, agent, 1)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_GEN_ERROR, atoi(agent->agent_id));
            sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
            goto end;
        }
        sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);
    }

    retval = 0;
end:
    wm_vuldet_free_cpe_list(node_list);
    os_free(node_list);
    if (obj) {
        cJSON_Delete(obj);
    }

    return retval;
}

void *wm_vuldet_main(wm_vuldet_t * vuldet) {
    wm_vuldet_init(vuldet);

    wm_vuldet_check_db();

    while (1) {
        // Update CVE databases
        if (vuldet->flags.update && wm_vuldet_run_update(vuldet->updates)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_OVAL_UPDATE_ERROR);
        }

        if ((vuldet->last_detection + (time_t) vuldet->detection_interval) < time(NULL)) {
            wm_vuldet_run_scan(vuldet);
        }

        wm_vuldet_run_sleep(vuldet);
    }

    return NULL;
}

int wm_vuldet_set_agents_info(agent_software **agents_software, update_node **updates) {
    agent_software *agents = NULL;
    agent_software *f_agent = NULL;
    char global_db[OS_FLSIZE + 1];
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int dist_error;
    char *id;
    char *name;
    char *register_ip;
    char *os_name;
    char *os_version;
    char *arch;
    int build;
    vu_feed agent_dist_ver;
    vu_feed agent_dist;

    snprintf(global_db, OS_FLSIZE, "%s%s/%s", isChroot() ? "/" : "", WDB_DIR, WDB_GLOB_NAME);

    if (sqlite3_open_v2(global_db, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GLOBALDB_ERROR " SQL Error: '%s'", sqlite3_errmsg(db));
        return OS_INVALID;
    }

    sqlite3_busy_timeout(db, SQL_BUSY_SLEEP_MS);

    // Extracts the operating system of the agents
    if (wm_vuldet_prepare(db, vu_queries[VU_GLOBALDB_REQUEST], -1, &stmt, NULL)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GLOBALDB_ERROR);
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, DISCON_TIME);

    while (wm_vuldet_step(stmt) == SQLITE_ROW) {
        dist_error = -1;
        os_name = (char *) sqlite3_column_text(stmt, 0);
        os_version = (char *) sqlite3_column_text(stmt,1);
        name = (char *) sqlite3_column_text(stmt, 2);
        id = (char *) sqlite3_column_text(stmt, 3);
        build = sqlite3_column_int(stmt, 6);

        if (id == NULL) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_NULL_AGENT_ID);
            continue;
        }

        if (!os_name) {
            if (name) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AG_NEVER_CON, name);
            }
            continue;
        } else if (!os_version) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_UNSOPPORTED, atoi(id), name);
            continue;
        }

        if (strcasestr(os_name, vu_feed_ext[FEED_UBUNTU])) {
            if (strstr(os_version, "20")) {
                agent_dist_ver = FEED_FOCAL;
            } else if (strstr(os_version, "18")) {
                agent_dist_ver = FEED_BIONIC;
            } else if (strstr(os_version, "16")) {
                agent_dist_ver = FEED_XENIAL;
            } else if (strstr(os_version, "14")) {
                agent_dist_ver = FEED_TRUSTY;
            } else if (strstr(os_version, "12")) {
                agent_dist_ver = FEED_PRECISE;
            } else {
                dist_error = FEED_UBUNTU;
            }
            agent_dist = FEED_UBUNTU;
        } else if (strcasestr(os_name, vu_feed_ext[FEED_DEBIAN])) {
            if (strstr(os_version, "7")) {
                agent_dist_ver = FEED_WHEEZY;
            } else if (strstr(os_version, "8")) {
                agent_dist_ver = FEED_JESSIE;
            } else if (strstr(os_version, "9")) {
                agent_dist_ver = FEED_STRETCH;
            } else if (strstr(os_version, "10")) {
                agent_dist_ver = FEED_BUSTER;
            } else {
                dist_error = FEED_DEBIAN;
            }
            agent_dist = FEED_DEBIAN;
        }  else if (strcasestr(os_name, vu_feed_ext[FEED_REDHAT])) {
            if (strstr(os_version, "8")) {
                agent_dist_ver = FEED_RHEL8;
            } else if (strstr(os_version, "7")) {
                agent_dist_ver = FEED_RHEL7;
            } else if (strstr(os_version, "6")) {
                agent_dist_ver = FEED_RHEL6;
            } else if (strstr(os_version, "5")) {
                agent_dist_ver = FEED_RHEL5;
            } else {
                dist_error = FEED_REDHAT;
            }
            agent_dist = FEED_REDHAT;
        } else if (strcasestr(os_name, vu_feed_ext[FEED_CENTOS])) {
            if (strstr(os_version, "8")) {
                agent_dist_ver = FEED_RHEL8;
            } else if (strstr(os_version, "7")) {
                agent_dist_ver = FEED_RHEL7;
            } else if (strstr(os_version, "6")) {
                agent_dist_ver = FEED_RHEL6;
            } else if (strstr(os_version, "5")) {
                agent_dist_ver = FEED_RHEL5;
            } else {
                dist_error = FEED_CENTOS;
            }
            agent_dist = FEED_REDHAT;
        } else if (strcasestr(os_name, vu_feed_ext[FEED_WIN])) {
            agent_dist_ver = wm_vuldet_decode_win_os(os_name);
            agent_dist = FEED_WIN;
        } else {
            // Operating system not supported in any of its versions
            dist_error = -2;
        }

        if (dist_error != -1) {
            // Check if the agent OS can be matched with a OVAL
            if (agent_dist_ver = wm_vuldet_set_allowed_feed(os_name, os_version, updates, &agent_dist), agent_dist_ver == FEED_UNKNOWN) {
                if (dist_error == -2) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_UNSOPPORTED, atoi(id), name);
                    continue;
                } else {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UNS_OS_VERSION, atoi(id), vu_feed_ext[dist_error]);
                    continue;
                }
            }
        }

        register_ip = (char *) sqlite3_column_text(stmt, 4);
        if (!register_ip) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_NULL_AGENT_IP, atoi(id));
            continue;
        }

        arch = (char *) sqlite3_column_text(stmt, 5);

        if (!agents) {
            os_calloc(1, sizeof(agent_software), agents);
            f_agent = agents;
        } else {
            os_calloc(1, sizeof(agent_software), agents->next);
            agents = agents->next;
        }

        agents->next = NULL;

        os_strdup(register_ip, agents->agent_ip);

        agents->build = build;
        os_strdup(id, agents->agent_id);

        os_strdup(name, agents->agent_name);
        if (arch) {
            os_strdup(arch, agents->arch);
        }
        agents->dist = agent_dist;
        agents->dist_ver = agent_dist_ver;
        agents->info = 0;
    }

    wdb_finalize(stmt);
    *agents_software = f_agent;
    sqlite3_close_v2(db);
    return 0;
}

void wm_vuldet_destroy(wm_vuldet_t * vuldet) {
    agent_software *agent;
    update_node **update;
    int i, j, k;

    for (i = 0, update = vuldet->updates; i < OS_SUPP_SIZE; i++) {
        if (update[i]) {
            free(update[i]->dist);
            free(update[i]->version);
            free(update[i]->url);
            free(update[i]->path);
            if (wm_vuldet_is_single_provider(update[i]->dist_ref)) {
                if (update[i]->allowed_os_name) {
                    for (j = 0; update[i]->allowed_os_name[j]; j++) {
                        free(update[i]->allowed_os_name[j]);
                        free(update[i]->allowed_os_ver[j]);
                    }
                    free(update[i]->allowed_os_name);
                    free(update[i]->allowed_os_ver);
                }
            } else {
                if (update[i]->allowed_multios_src_name) {
                    for (j = 0; update[i]->allowed_multios_src_name[j]; j++) {
                        for (k = 0; update[i]->allowed_multios_src_name[j][k]; k++) {
                            free(update[i]->allowed_multios_src_name[j][k]);
                            free(update[i]->allowed_multios_src_ver[j][k]);
                        }
                        free(update[i]->allowed_multios_src_name[j]);
                        free(update[i]->allowed_multios_src_ver[j]);
                    }
                    free(update[i]->allowed_multios_src_name);
                    free(update[i]->allowed_multios_src_ver);

                    for (j = 0; update[i]->allowed_os_name[j]; j++) {
                        free(update[i]->allowed_multios_dst_name[j]);
                        free(update[i]->allowed_multios_dst_ver[j]);
                    }
                    free(update[i]->allowed_multios_dst_name);
                    free(update[i]->allowed_multios_dst_ver);
                }
            }
            free(update[i]);
        }
    }

    for (agent = vuldet->agents_software; agent;) {
        agent_software *agent_aux = agent->next;
        wm_vuldet_free_agent_software(agent);
        if (agent_aux) {
            agent = agent_aux;
        } else {
            break;
        }
    }
    free(vuldet);
}


cJSON *wm_vuldet_dump(const wm_vuldet_t * vuldet){
    cJSON *root = cJSON_CreateObject();
    cJSON *wm_vd = cJSON_CreateObject();
    unsigned int i;

    if (vuldet->flags.enabled) cJSON_AddStringToObject(wm_vd,"enabled","yes"); else cJSON_AddStringToObject(wm_vd,"enabled","no");
    if (vuldet->flags.run_on_start) cJSON_AddStringToObject(wm_vd,"run_on_start","yes"); else cJSON_AddStringToObject(wm_vd,"run_on_start","no");
    cJSON_AddNumberToObject(wm_vd,"interval",vuldet->detection_interval);
    cJSON_AddNumberToObject(wm_vd,"ignore_time",vuldet->ignore_time);
    cJSON *providers = cJSON_CreateArray();

    for (i = 0; i < OS_SUPP_SIZE; i++) {
        if (vuldet->updates[i] && i != CPE_WDIC && i != CVE_MSU) {
            cJSON *provider = cJSON_CreateObject();
            if (vuldet->updates[i]->dist) cJSON_AddStringToObject(provider,"name",vuldet->updates[i]->dist);
            if (vuldet->updates[i]->version) cJSON_AddStringToObject(provider,"version",vuldet->updates[i]->version);
            if (vuldet->updates[i]->dist && (!strcmp(vuldet->updates[i]->dist, "redhat") || !strcmp(vuldet->updates[i]->dist, "nvd"))) {
                if (vuldet->updates[i]->update_from_year) cJSON_AddNumberToObject(provider,"update_from_year",vuldet->updates[i]->update_from_year);
            }
            if (vuldet->updates[i]->url) {     // URL for Canonical and Debian providers
                cJSON_AddStringToObject(provider,"url",vuldet->updates[i]->url);
            } else if (vuldet->updates[i]->multi_url) {     // URL for RedHat and NVD providers
                cJSON_AddStringToObject(provider,"url",vuldet->updates[i]->multi_url);
                cJSON *url_attrs = cJSON_CreateObject();
                int add = 0;
                if (vuldet->updates[i]->multi_url_start > 0) {
                    cJSON_AddNumberToObject(url_attrs,"start",vuldet->updates[i]->multi_url_start);
                    add = 1;
                }
                if (vuldet->updates[i]->multi_url_end > 0) {
                    cJSON_AddNumberToObject(url_attrs,"end",vuldet->updates[i]->multi_url_end);
                    add = 1;
                }
                if (vuldet->updates[i]->port > 0) {
                    cJSON_AddNumberToObject(url_attrs,"port",vuldet->updates[i]->port);
                    add = 1;
                }
                if (add) {
                    cJSON_AddItemToObject(provider,"url_attrs",url_attrs);
                } else {
                    cJSON_Delete(url_attrs);
                }
            }
            if (vuldet->updates[i]->path) {
                cJSON_AddStringToObject(provider,"path",vuldet->updates[i]->path);
            } else if (vuldet->updates[i]->multi_path){
                cJSON_AddStringToObject(provider,"path",vuldet->updates[i]->multi_path);
            }

            cJSON_AddNumberToObject(provider,"update_interval",vuldet->updates[i]->interval);
            cJSON_AddNumberToObject(provider,"download_timeout",vuldet->updates[i]->timeout);

            // Allowed OS
            if (wm_vuldet_is_single_provider(vuldet->updates[i]->dist_ref)) {
                if (vuldet->updates[i]->allowed_os_name && vuldet->updates[i]->allowed_os_name[0]) {
                    int j = 0;
                    cJSON *allow = cJSON_CreateArray();
                    cJSON *allowed = cJSON_CreateObject();
                    cJSON *src = cJSON_CreateArray();
                    for (j = 0; vuldet->updates[i]->allowed_os_name[j]; j++) {
                        char * src_aux;
                        os_calloc(OS_SIZE_128, sizeof(char), src_aux);
                        // It is mandatory to have name + version, the only valid format is name-version
                        snprintf(src_aux, OS_SIZE_128 - 1, "%s-%s", vuldet->updates[i]->allowed_os_name[j], vuldet->updates[i]->allowed_os_ver[j]);
                        cJSON_AddItemToArray(src, cJSON_CreateString(src_aux));
                        os_free(src_aux);
                    }
                    cJSON_AddItemToObject(allowed, "src", src);
                    cJSON_AddItemToArray(allow, allowed);
                    cJSON_AddItemToObject(provider,"allow",allow);
                }
            } else if (vuldet->updates[i]->allowed_multios_src_name) {
                int j = 0;
                cJSON *allow = cJSON_CreateArray();
                for (j = 0; vuldet->updates[i]->allowed_multios_src_name[j]; j++) {
                    cJSON *allowed = cJSON_CreateObject();
                    if (vuldet->updates[i]->allowed_multios_dst_name && vuldet->updates[i]->allowed_multios_dst_name[j]) {
                        char * dst_os;
                        os_calloc(OS_SIZE_128, sizeof(char), dst_os);
                        if (vuldet->updates[i]->allowed_multios_dst_ver && vuldet->updates[i]->allowed_multios_dst_ver[j]) {
                            snprintf(dst_os, OS_SIZE_128 - 1, "%s-%s", vuldet->updates[i]->allowed_multios_dst_name[j], vuldet->updates[i]->allowed_multios_dst_ver[j]);
                        } else {
                            snprintf(dst_os, OS_SIZE_128 - 1, "%s", vuldet->updates[i]->allowed_multios_dst_name[j]);
                        }
                        cJSON_AddStringToObject(allowed,"replaced_os",dst_os);
                        os_free(dst_os);
                    }
                    int k = 0;
                    cJSON *src = cJSON_CreateArray();
                    for (k = 0; vuldet->updates[i]->allowed_multios_src_name[j][k]; k++) {
                        char * src_os;
                        os_calloc(OS_SIZE_128, sizeof(char), src_os);
                        if (vuldet->updates[i]->allowed_multios_src_ver && vuldet->updates[i]->allowed_multios_src_ver[j] && vuldet->updates[i]->allowed_multios_src_ver[j][k]) {
                            snprintf(src_os, OS_SIZE_128 - 1, "%s-%s", vuldet->updates[i]->allowed_multios_src_name[j][k], vuldet->updates[i]->allowed_multios_src_ver[j][k]);
                        } else {
                            snprintf(src_os, OS_SIZE_128 - 1, "%s", vuldet->updates[i]->allowed_multios_src_name[j][k]);
                        }
                        cJSON_AddItemToArray(src, cJSON_CreateString(src_os));
                        os_free(src_os);
                    }
                    cJSON_AddItemToObject(allowed, "src", src);
                    cJSON_AddItemToArray(allow, allowed);
                }
                cJSON_AddItemToObject(provider,"allow",allow);
            }

            cJSON_AddItemToArray(providers, provider);
        }
    }

    if (cJSON_GetArraySize(providers) > 0) {
        cJSON_AddItemToObject(wm_vd,"providers",providers);
    } else {
        cJSON_free(providers);
    }

    cJSON_AddItemToObject(root,"vulnerability-detector",wm_vd);

    return root;

}

char *vu_get_version() {
    static char version[10] = { '\0' };
    int major = 0;
    int minor = 0;
    int patch = 0;

    if (*version == '\0') {
        sscanf(__ossec_version, "v%d.%d.%d", &major, &minor, &patch);
        snprintf(version, 9, "%d%d%d", major, minor, patch);
    }

    return version;
}

int wm_vuldet_db_empty(sqlite3 *db, vu_feed version) {

    sqlite3_stmt *stmt = NULL;
    int result = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_CHECK_DB_CONTENT], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[version], -1, NULL);

    if (result = wm_vuldet_step(stmt), result == SQLITE_DONE ||
        (result == SQLITE_ROW && !sqlite3_column_int(stmt, 0))) {
        result = 0;
    } else {
        result = 1;
    }

    wdb_finalize(stmt);

    return result;
}

int wm_vuldet_nvd_empty() {
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    int result;

    if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, NULL);
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_COUNT], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    if (result = wm_vuldet_step(stmt), result == SQLITE_DONE ||
        (result == SQLITE_ROW && !sqlite3_column_int(stmt, 0))) {
        // NVD DB empty
        result = 0;
    } else {
        result = 1;
    }

    wdb_finalize(stmt);
    sqlite3_close_v2(db);

    return result;
}

const char *wm_vuldet_get_unified_severity(char *severity) {

    if (!severity || strcasestr(severity, vu_severities[VU_UNKNOWN]) || strcasestr(severity, vu_severities[VU_UNTR])) {
        return vu_severities[VU_UNDEFINED_SEV];
    } else if (strcasestr(severity, vu_severities[VU_LOW]) || strcasestr(severity, vu_severities[VU_NEGL])) {
        return vu_severities[VU_LOW];
    } else if (strcasestr(severity, vu_severities[VU_MEDIUM]) || strcasestr(severity, vu_severities[VU_MODERATE])) {
        return vu_severities[VU_MEDIUM];
    } else if (strcasestr(severity, vu_severities[VU_HIGH]) || strcasestr(severity, vu_severities[VU_IMPORTANT])) {
        return vu_severities[VU_HIGH];
    } else if (strcasestr(severity, vu_severities[VU_CRITICAL])) {
        return vu_severities[VU_CRITICAL];
    } else if (strcasestr(severity, vu_severities[VU_NONE])) {
        return vu_severities[VU_NONE];
    }
    return vu_severities[VU_UNDEFINED_SEV];

}

void wm_vuldet_get_package_os(const char *version, const char **os_major, char **os_minor)  {
    char *v_it;

    if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_8]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL8];
    } else if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_7]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL7];
    } else if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_6]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL6];
    } else if (v_it = strstr(version, vu_package_dist[VU_RH_EXT_5]), v_it) {
        *os_major = vu_feed_tag[FEED_RHEL5];
    }

    wm_vuldet_set_subversion(v_it, os_minor);
}

void wm_vuldet_set_subversion(char *version, char **os_minor) {
    if (version && (version = strchr(version, '_')) && *(++version) != '\0') {
        size_t minor_size = strlen(version) + 1;
        os_calloc(minor_size + 1, sizeof(char), *os_minor);
        snprintf(*os_minor, minor_size, "%ld", strtol(version, NULL, 10));
    }
}

int wm_vuldet_generate_os_cpe(sqlite3 *db, agent_software *agent, int *min_cpe_index, cpe_list **node_list) {
    if (agent->dist_ver >= FEED_WS2003 && agent->dist_ver <= FEED_WS2019) {
        // It is a supported Windows agent
        if (!wm_vuldet_generate_win_cpe(agent)) {
            int i;
            for (i = 0; agent->agent_os_cpe[i]; i++) {
                if (wm_vuldet_insert_agent_data(db, agent, min_cpe_index, "",
                                            vu_feed_ext[agent->dist_ver], "", "", NULL, "",
                                            agent->agent_os_cpe[i], node_list)) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR, agent->agent_os_cpe[i]->raw);
                    return OS_INVALID;
                }
            }
        }
    }

    return 0;
}

int wm_vuldet_generate_win_cpe(agent_software *agent) {
    STATIC const char *PR_WINXP = "windows_xp";
    STATIC const char *PR_WINVISTA = "windows_vista";
    STATIC const char *PR_WIN7 = "windows_7";
    STATIC const char *PR_WIN8 = "windows_8";
    STATIC const char *PR_WIN81 = "windows_8.1";
    STATIC const char *PR_WIN10 = "windows_10";
    STATIC const char *PR_WIN2003 = "windows_server_2003";
    STATIC const char *PR_WIN2008 = "windows_server_2008";
    STATIC const char *PR_WIN2012 = "windows_server_2012";
    STATIC const char *PR_WIN2016 = "windows_server_2016";
    STATIC const char *PR_WIN2019 = "windows_server_2019";
    STATIC const char *VER_R2 = "r2";
    const char *win_pr = NULL;
    const char *win_ver = NULL;
    const char *win_upd = NULL;

    if (!agent->os_release && agent->dist_ver != FEED_WS2012 && agent->dist_ver != FEED_WS2012R2) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OSINFO_DISABLED, atoi(agent->agent_id));
    }

    switch (agent->dist_ver) {
        case FEED_WS2003:
            win_pr = PR_WIN2003;
            win_upd = agent->os_release;
        break;
        case FEED_WS2003R2:
            win_pr = PR_WIN2003;
            win_ver = VER_R2;
            win_upd = agent->os_release;
        break;
        case FEED_WXP:
            win_pr = PR_WINXP;
            win_upd = agent->os_release;
        break;
        case FEED_WVISTA:
            win_pr = PR_WINVISTA;
            win_upd = agent->os_release;
        break;
        case FEED_W7:
            win_pr = PR_WIN7;
            win_upd = agent->os_release;
        break;
        case FEED_W8:
            win_pr = PR_WIN8;
            win_upd = agent->os_release;
        break;
        case FEED_W81:
            win_pr = PR_WIN81;
            win_upd = agent->os_release;
        break;
        case FEED_W10:
            win_pr = PR_WIN10;
            win_ver = agent->os_release;
        break;
        case FEED_WS2008:
            win_pr = PR_WIN2008;
            win_upd = agent->os_release;
        break;
        case FEED_WS2008R2:
            win_pr = PR_WIN2008;
            win_ver = VER_R2;
            win_upd = agent->os_release;
        break;
        case FEED_WS2012:
            win_pr = PR_WIN2012;
        break;
        case FEED_WS2012R2:
            win_pr = PR_WIN2012;
            win_ver = VER_R2;
        break;
        case FEED_WS2016:
            win_pr = PR_WIN2016;
            win_ver = agent->os_release;
        break;
        case FEED_WS2019:
            win_pr = PR_WIN2019;
            win_ver = agent->os_release;
        break;
        default:
            return 1;
    }

    // We will insert a double CPE for the following systems
    int os_cpes = wm_vuldet_double_os_cpe(agent->dist_ver) ? 2 : 1;
    os_calloc(os_cpes + 1, sizeof(cpe *), agent->agent_os_cpe);

    int i;
    for (i = 0; i < os_cpes; i++) {
        agent->agent_os_cpe[i] = wm_vuldet_generate_cpe("o",
                                                    "microsoft",
                                                    win_pr,
                                                    !i && win_ver ? win_ver : "-",
                                                    !i ? win_upd : win_ver,
                                                    NULL,
                                                    NULL,
                                                    NULL,
                                                    NULL,
                                                    agent->arch,
                                                    NULL,
                                                    0,
                                                    vu_feed_ext[agent->dist_ver]);
    }
    return 0;
}

int wm_vuldet_discard_kernel_package(agent_software *agent, const char *name,
                                    const char *version, const char *arch) {
    char *kernel_uname = NULL;

    if (agent->dist == FEED_REDHAT) {
        // Only kernel images have as package name "kernel".
        // "kernel-rt" is a realtime kernel only available for RedHat.
        if (strcmp(name, "kernel") && strcmp(name, "kernel-rt")) {
            return 0;
        }
        size_t len = snprintf(kernel_uname, 0, "%s.%s", version, arch);
        os_calloc(1, len + 1, kernel_uname);
        snprintf(kernel_uname, len + 1, "%s.%s", version, arch);

    } else { // For Debian family
        os_calloc(1, strlen(name) + 1, kernel_uname);
        if (!sscanf(name, "linux-image-%s", kernel_uname) || strstr(name, "linux-image-generic")) {
            os_free(kernel_uname);
            return 0;
        }
    }

    if (strcmp(kernel_uname, agent->kernel_release)) {
        mdebug2(VU_DISCARD_KERNEL_PKG, kernel_uname, atoi(agent->agent_id));
        os_free(kernel_uname);
        return -1;
    }

    os_free(kernel_uname);

    return 0;
}

int wm_vuldet_generate_os_and_kernel_package(sqlite3 *db, agent_software *agent) {
    const char *product_name = NULL;
    const char *vendor = NULL;

    if (!db) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_DB_NOT_INITIALIZED);
        return OS_INVALID;
    }

    if (!agent || !agent->agent_id) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_AGENT_NOT_INITIALIZED);
        return OS_INVALID;
    }

    if (!agent->os_release && !agent->kernel_release) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_OSINFOLNX_DISABLED, atoi(agent->agent_id));
    }

    switch (agent->dist_ver) {
        case FEED_PRECISE:
        case FEED_TRUSTY:
        case FEED_XENIAL:
        case FEED_BIONIC:
        case FEED_FOCAL:
            product_name = PR_UBUNTU;
            vendor = V_UBUNTU;
        break;
        case FEED_JESSIE:
        case FEED_STRETCH:
        case FEED_BUSTER:
        case FEED_WHEEZY:
            product_name = PR_DEBIAN;
            vendor = V_DEBIAN;
        break;
        case FEED_RHEL5:
        case FEED_RHEL6:
        case FEED_RHEL7:
        case FEED_RHEL8:
            product_name = PR_REDHAT;
            vendor = V_REDHAT;
        break;
        default:
            return 1;
    }

    // Generate os package
    if (agent->os_release) {
        if (wm_vuldet_insert_agent_data(db, agent, NULL, vendor, product_name, NULL, agent->os_release, NULL, agent->arch, NULL, NULL)) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR, product_name);
            return OS_INVALID;
        }
    }

    // Generate kernel package
    if (agent->kernel_release) {
        char *release, *release_end;
        os_strdup(agent->kernel_release, release);
        if (release_end = strstr(release, "-"), release_end) {
            *release_end = '\0';
        }

        if (wm_vuldet_insert_agent_data(db, agent, NULL, V_KERNEL, PR_KERNEL, NULL, release, NULL, agent->arch, NULL, NULL)) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_INSERT_DATA_ERR, PR_KERNEL);
            os_free(release);
            return OS_INVALID;
        }

        os_free(release);
    }

    return 0;
}

vu_feed wm_vuldet_decode_win_os(char *os_raw) {
    if (strcasestr(os_raw, vu_feed_ext[FEED_WS2003R2])) {
        return FEED_WS2003R2;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2003])) {
        return FEED_WS2003;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WXP])) {
        return FEED_WXP;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WVISTA])) {
        return FEED_WVISTA;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W7])) {
        return FEED_W7;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W81])) {
        return FEED_W81;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W8])) {
        return FEED_W8;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_W10])) {
        return FEED_W10;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2008R2])) {
        return FEED_WS2008R2;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2008])) {
        return FEED_WS2008;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2012R2])) {
        return FEED_WS2012R2;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2012])) {
        return FEED_WS2012;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2016])) {
        return FEED_WS2016;
    } else if (strcasestr(os_raw, vu_feed_ext[FEED_WS2019])) {
        return FEED_WS2019;
    } else {
        STATIC OSHash *unk_os = NULL;
        STATIC int os_count = 0;

        if (!os_count) {
            if (unk_os = OSHash_Create(), !unk_os) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_CREATE_HASH_ERRO, "unknown_systems");
                pthread_exit(NULL);
            }
        }

        if (os_count < 20 && OSHash_Add(unk_os, os_raw, NULL) == 2) {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_UNC_SYSTEM, os_raw);
            os_count++;
        }
        return FEED_WIN;
    }
}

int wm_vuldet_insert_agent_data(sqlite3 *db, agent_software *agent, int *cpe_index, const char *vendor, const char *product, const char *source, const char *version, const char *src_version, const char *arch, cpe *ag_cpe, cpe_list **node_list) {
    sqlite3_stmt *stmt = NULL;
    int retval = OS_INVALID;
    int result;
    const char *os_major = NULL;
    char *os_minor = NULL;
    char *normalized_name = NULL;
    char *normalized_source = NULL;

    if (agent->dist == FEED_REDHAT) {
        wm_vuldet_get_package_os(version, &os_major, &os_minor);
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_AGENTS], -1, &stmt, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, stmt);
        goto end;
    }

    sqlite3_bind_text(stmt, 1, agent->agent_id, -1, NULL);
    sqlite3_bind_text(stmt, 2, os_major, -1, NULL);
    sqlite3_bind_text(stmt, 3, os_minor, -1, NULL);
    sqlite3_bind_int(stmt, 4, ag_cpe ? *cpe_index : 0);
    sqlite3_bind_text(stmt, 5, vendor, -1, NULL);

    if (agent->dist != FEED_WIN) {
        /* In Windows the package name is
           normalized by the CPE helper */
        normalized_name = w_tolower_str(product);
        sqlite3_bind_text(stmt, 6, normalized_name, -1, NULL);

        normalized_source = w_tolower_str(source);
        sqlite3_bind_text(stmt, 7, normalized_source, -1, NULL);
    } else {
        sqlite3_bind_text(stmt, 6, product, -1, NULL);
        sqlite3_bind_text(stmt, 7, source, -1, NULL);
    }

    sqlite3_bind_text(stmt, 8, version, -1, NULL);
    sqlite3_bind_text(stmt, 9, src_version, -1, NULL);
    sqlite3_bind_text(stmt, 10, arch, -1, NULL);

    if (ag_cpe) {
        if (!*node_list) {
            os_calloc(1, sizeof(cpe_list), *node_list);
        }

        if (wm_vuldet_add_cpe(ag_cpe, NULL, *node_list, *cpe_index)) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_PARSE_ERROR, ag_cpe->raw, atoi(agent->agent_id));
        } else {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_RECV, ag_cpe->raw, atoi(agent->agent_id));
            *cpe_index = *cpe_index - 1;
        }
    }

    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        wm_vuldet_sql_error(db, stmt);
        goto end;
    }

    retval = 0;

end:
    os_free(os_minor);
    os_free(normalized_name);
    os_free(normalized_source);
    wdb_finalize(stmt);
    return retval;
}

void wm_vuldet_free_agent_software(agent_software *agent) {
    if (agent) {
        free(agent->agent_id);
        free(agent->agent_name);
        free(agent->arch);
        free(agent->agent_ip);
        free(agent->os_release);
        free(agent->kernel_release);
        int i;
        for (i = 0; agent->agent_os_cpe && agent->agent_os_cpe[i]; i++) {
            wm_vuldet_free_cpe(agent->agent_os_cpe[i]);
        }
        free(agent->agent_os_cpe);
        free(agent);
    }
}

int wm_vuldet_fetch_MSU() {
    if (w_uncompress_gzfile(VU_MSU_FILE_GZ, VU_FIT_TEMP_FILE)) {
        return VU_INV_FEED;
    }

    return 0;
}


int wm_vuldet_fetch_wazuh_cpe(update_node *update) {
    int retval = VU_INV_FEED;
    char buffer[OS_MAXSTR + 1];
    FILE *fp = NULL;
    FILE *fp_out;
    STATIC const char *UPDATE_DATE_TAG = "update_date";

    if (fp = fopen(VU_CPE_HELPER_FILE, "r"), !fp) {
        goto end;
    }
    if (fp_out = fopen(VU_FIT_TEMP_FILE, "w"), !fp_out) {
        goto end;
    }

    while (fgets(buffer, OS_MAXSTR, fp)) {
        if (*buffer == '=') {
            continue;
        }
        fwrite(buffer, 1, strlen(buffer), fp_out);
    }
    fclose(fp);
    fclose(fp_out);

    if (fp = fopen(VU_FIT_TEMP_FILE, "r"), !fp) {
        goto end;
    }

    // Check the timestamp
    while (fgets(buffer, OS_MAXSTR, fp)) {
        char *timestamp_base;
        char *timestamp_end;

        if ((timestamp_base = strstr(buffer, UPDATE_DATE_TAG)) &&
            (timestamp_base = strchr(timestamp_base, ':')) &&
            (timestamp_base = strchr(timestamp_base, '\"')) &&
            (timestamp_end = strchr(++timestamp_base, '\"'))) {
            char timestamp[OS_SIZE_256 + 1];

            *timestamp_end = '\0';
            switch (wm_vuldet_check_timestamp(vu_feed_tag[update->dist_tag_ref], timestamp_base, timestamp)) {
                case VU_TIMESTAMP_FAIL:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED_ERROR, update->dist_ext);
                    goto end;
                break;
                case VU_TIMESTAMP_UPDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, update->dist_ext, timestamp);
                    retval = VU_NOT_NEED_UPDATE;
                    goto end;
                break;
                case VU_TIMESTAMP_OUTDATED:
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED, update->dist_ext, "");
                break;
            }
            break;
        }
    }

    retval = 0;
end:
    if (fp) {
        fclose(fp);
    }

    return retval;
}

int wm_vuldet_json_wcpe_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    cJSON *json_it;
    vu_cpe_dic *wcpes_it = NULL;
    STATIC char *JSON_WCPE_FORMAT_VERSION = "format_version";
    STATIC char *JSON_WCPE_UPDATE_DATE = "update_date";
    STATIC char *JSON_WCPE_DICTIONARY = "dictionary";
    char *timestamp;
    char *format_version_major;
    int major_dec;

    // Check the timestamp
    if (json_it = cJSON_GetObjectItem(json_feed, JSON_WCPE_UPDATE_DATE), !json_it) {
        return OS_INVALID;
    }

    if (timestamp = json_it->valuestring, !timestamp) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_WCPE_GET_TIMEST_ERROR);
        return OS_INVALID;
    }

    switch (wm_vuldet_check_timestamp(vu_feed_tag[FEED_CPEW], timestamp, NULL)) {
        case VU_TIMESTAMP_FAIL:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED_ERROR, vu_feed_ext[FEED_CPEW]);
            return OS_INVALID;
        break;
        case VU_TIMESTAMP_UPDATED:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_DATE, vu_feed_ext[FEED_CPEW], timestamp);
            return VU_NOT_NEED_UPDATE;
        break;
        case VU_TIMESTAMP_OUTDATED:
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED, vu_feed_ext[FEED_CPEW], "");
        break;
    }

    // Check the version format
    if (json_it = cJSON_GetObjectItem(json_feed, JSON_WCPE_FORMAT_VERSION), !json_it) {
        return OS_INVALID;
    }

    if (format_version_major = json_it->valuestring, !format_version_major) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_WCPE_GET_VERFORMAT_ERROR);
        return OS_INVALID;
    }

    if (major_dec = strtol(format_version_major, NULL, 10), major_dec < 1 || major_dec > VU_CPEHELPER_FORMAT_VERSION) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_WCPE_INV_VERFORMAT, format_version_major);
        return OS_INVALID;
    }

    for (json_it = json_feed->child; json_tagged_obj(json_it); json_it = json_it->next) {
        if (!wcpes_it) {
            os_calloc(1, sizeof(vu_cpe_dic), wcpes_it);
        }
        if (!strcmp(json_it->string, vu_cpe_tags[CPE_VERSION])) {
            if (w_strdup(json_it->valuestring, wcpes_it->version)) {
                goto error;
            }
        } else if (!strcmp(json_it->string, JSON_WCPE_FORMAT_VERSION)) {
            if (w_strdup(json_it->valuestring, wcpes_it->format_version)) {
                goto error;
            }
        } else if (!strcmp(json_it->string, JSON_WCPE_UPDATE_DATE)) {
            if (w_strdup(json_it->valuestring, wcpes_it->update_date)) {
                goto error;
            }
        } else if (!strcmp(json_it->string, JSON_WCPE_DICTIONARY)) {
            wm_vuldet_json_dic_parser(json_it->child, wcpes_it);
        }
    }
    parsed_vulnerabilities->w_cpes = wcpes_it;

    return VU_NEED_UPDATE;
error:
    os_free(wcpes_it->version);
    os_free(wcpes_it->format_version);
    os_free(wcpes_it->update_date);
    os_free(wcpes_it);

    return OS_INVALID;
}

int wm_vuldet_json_dic_parser(cJSON *json_feed, vu_cpe_dic *wcpes) {
    cJSON *dic_node;
    char need_check_hotfix = 0;
    int i;
    STATIC char *JSON_WCPE_ACTION = "action";
    STATIC char *JSON_WCPE_TARGET = "target";
    STATIC char *JSON_WCPE_SOURCE = "source";
    STATIC char *JSON_WCPE_TRANSLATION = "translation";

    for (; json_feed; json_feed = json_feed->next) {
        char assigned = 0;
        vu_cpe_dic_node *node;
        os_calloc(1, sizeof(vu_cpe_dic_node), node);
        for (dic_node = json_feed->child; json_tagged_obj(dic_node); dic_node = dic_node->next) {
            if (!strcmp(dic_node->string, JSON_WCPE_TARGET) && dic_node->valuestring) {
                vu_cpe_dic_node *node_ref = NULL;

                for (i = 0; wcpes->targets_name && wcpes->targets_name[i]; i++) {
                    if (!strcmp(wcpes->targets_name[i], dic_node->valuestring)) {
                        node_ref = wcpes->targets[i];
                        break;
                    }
                }

                if (!node_ref) {
                    os_realloc(wcpes->targets_name, (i + 2) * sizeof(char *), wcpes->targets_name);
                    os_realloc(wcpes->targets, (i + 2) * sizeof(vu_cpe_dic_node *), wcpes->targets);
                    os_strdup(dic_node->valuestring, wcpes->targets_name[i]);
                    wcpes->targets[i + 1] = NULL;
                    wcpes->targets_name[i + 1] = NULL;
                } else {
                    node->prev = wcpes->targets[i];
                    wcpes->targets[i]->next = node;
                }

                wcpes->targets[i] = node;
                assigned = 1;
            } else if (!strcmp(dic_node->string, JSON_WCPE_ACTION)) {
                int msu_mask = CPE_DIC_REP_MSUN_IF_VER_MATCHES | CPE_DIC_REP_MSUN;

                node->action = wm_vuldet_fill_action_mask(dic_node->child);
                if ((node->action & msu_mask && !(node->action & CPE_DIC_CHECK_HOTFIX)) ||
                    (node->action & CPE_DIC_CHECK_HOTFIX && !(node->action & msu_mask))) {
                    need_check_hotfix = 1;
                }
            } else if (!strcmp(dic_node->string, JSON_WCPE_SOURCE)) {
                wm_vuldet_fill_dic_section(dic_node->child, &node->source);
            } else if (!strcmp(dic_node->string, JSON_WCPE_TRANSLATION)) {
                wm_vuldet_fill_dic_section(dic_node->child, &node->translation);
            }
        }

        if (!assigned) {
            os_free(node);
        }
    }

    if (need_check_hotfix) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_CPEHELPER_INVALID_ACTION, vu_cpe_dic_option[VU_REP_MSUN_IF_VER_MATCHES], vu_cpe_dic_option[VU_CHECK_HOTFIX]);
    }

    return 0;
}

int wm_vuldet_fill_dic_section(cJSON *json_feed, vu_cpe_dic_section **wcpes) {
    cJSON *json_array;

    if (!*wcpes) {
        os_calloc(1, sizeof(vu_cpe_dic_section), *wcpes);
    }

    for (; json_tagged_obj(json_feed); json_feed = json_feed->next) {
        char ****array;
        int i;

        if (!strcmp(json_feed->string, vu_cpe_tags[CPE_VENDOR])) {
            array = &(*wcpes)->vendor;
            if (!json_feed->child) {
                os_realloc(*array, 2 * sizeof(char **), *array);
                (*array)[1] = NULL;
                os_calloc(2, sizeof(char *), (*array)[0]);
                os_strdup("", ***array);
            }
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_PRODUCT])) {
            array = &(*wcpes)->product;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_VERSION])) {
            array = &(*wcpes)->version;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_SW_EDITION])) {
            array = &(*wcpes)->sw_edition;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_TARGET_HW])) {
            array = &(*wcpes)->target_hw;
        } else if (!strcmp(json_feed->string, vu_cpe_tags[CPE_MSU_NAME])) {
            array = &(*wcpes)->msu_name;
        } else {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_INVALID_DIC_TAG, json_feed->string);
            continue;
        }

        for (i = 0, json_array = json_feed->child; json_array; json_array = json_array->next, i++) {
            os_realloc(*array, (i + 2) * sizeof(char **), *array);
            memset(&(*array)[i], 0, 2 * sizeof(char **));

            if (json_array->type == cJSON_String) {
                os_calloc(2, sizeof(char *), (*array)[i]);
                w_strdup(json_array->valuestring, (*array)[i][0]);
            } else if (json_array->type == cJSON_Array) {
                cJSON *json_sub_array = json_array->child;
                int j = 0;

                for (; json_sub_array; json_sub_array = json_sub_array->next, j++) {
                    if (json_sub_array->type != cJSON_String) {
                        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_ENTRY_TYPE, -1 * json_array->type);
                        j--;
                    } else {
                        os_realloc((*array)[i], (j + 2) * sizeof(char *), (*array)[i]);
                        w_strdup(json_sub_array->valuestring, (*array)[i][j]);
                        (*array)[i][j + 1] = NULL;
                    }
                }
            } else {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_ENTRY_TYPE, json_array->type);
                i--;
            }
        }
    }

    return 0;
}

unsigned int wm_vuldet_fill_action_mask(cJSON *json_feed) {
    unsigned int mask = 0;

    for (; json_feed && json_feed->valuestring; json_feed = json_feed->next) {
        if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_VEN])) {
            mask |= CPE_DIC_REP_VEN;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_PROD])) {
            mask |= CPE_DIC_REP_PROD;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_VEN_IF_MATCH])) {
            mask |= CPE_DIC_REP_VEN_IF_MATCH;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_PROD_IF_MATCH])) {
            mask |= CPE_DIC_REP_PROD_IF_MATCH;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_VER_IF_MATCHES])) {
            mask |= CPE_DIC_SET_VER_IF_MATCH;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_SWED_VER_IF_PR_MATCHES])) {
            mask |= CPE_DIC_REP_SWED_IF_PROD_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_MSUN_IF_VER_MATCHES])) {
            mask |= CPE_DIC_REP_MSUN_IF_VER_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_IGNORE])) {
            mask |= CPE_DIC_IGNORE;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_CHECK_HOTFIX])) {
            mask |= CPE_DIC_CHECK_HOTFIX;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_REP_MSUN])) {
            mask |= CPE_DIC_REP_MSUN;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_VERSION_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_VER_IF_PROD_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_TARGETHW_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_TARGETHW_IF_PRODUCT_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_VER_ONLY_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_VER_ONLY_IF_PROD_MATCHES;
        } else if (!strcmp(json_feed->valuestring, vu_cpe_dic_option[VU_SET_TARGETHW_ONLY_IF_PROD_MATCHES])) {
            mask |= CPE_DIC_SET_TARGETHW_ONLY_IF_PRODUCT_MATCHES;
        } else {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_INVALID_DIC_TAG, json_feed->valuestring);
        }
    }

    return mask;
}

void wm_vuldet_json_msu_parser_vuln(cJSON *json_feed, vu_msu_vul_entry **msu) {
    cJSON *vulnerability;
    cJSON *vuln_node;
    STATIC char *JSON_MSU_PATCH = "patch";
    STATIC char *JSON_MSU_PRODUCT = "product";
    STATIC char *JSON_MSU_RESTART = "restart_required";
    STATIC char *JSON_MSU_SUBTYPE = "subtype";
    STATIC char *JSON_MSU_TITLE = "title";
    STATIC char *JSON_MSU_URL = "url";

    if (json_feed = cJSON_GetObjectItem(json_feed, "vulnerabilities"), !json_feed) {
        return;
    }

    for (json_feed = json_feed->child; json_tagged_obj(json_feed); json_feed = json_feed->next) {
        for (vuln_node = json_feed->child; vuln_node; vuln_node = vuln_node->next) {
            if (*msu) {
                os_calloc(1, sizeof(vu_msu_vul_entry), (*msu)->next);
                (*msu)->next->prev = *msu;
                *msu = (*msu)->next;
            } else {
                os_calloc(1, sizeof(vu_msu_vul_entry), *msu);
            }

            os_strdup(json_feed->string, (*msu)->cveid);
            for (vulnerability = vuln_node->child; vulnerability; vulnerability = vulnerability->next) {
                if (!vulnerability->valuestring) {
                    continue;
                }

                if (!strcmp(vulnerability->string, JSON_MSU_PATCH)) {
                    os_strdup(vulnerability->valuestring, (*msu)->patch);
                } else if (!strcmp(vulnerability->string, JSON_MSU_RESTART)) {
                    os_strdup(vulnerability->valuestring, (*msu)->restart_required);
                } else if (!strcmp(vulnerability->string, JSON_MSU_PRODUCT)) {
                    os_strdup(vulnerability->valuestring, (*msu)->product);
                    wm_vuln_check_msu_type(*msu, json_feed->child);
                } else if (!strcmp(vulnerability->string, JSON_MSU_SUBTYPE)) {
                    os_strdup(vulnerability->valuestring, (*msu)->subtype);
                } else if (!strcmp(vulnerability->string, JSON_MSU_TITLE)) {
                    os_strdup(vulnerability->valuestring, (*msu)->title);
                } else if (!strcmp(vulnerability->string, JSON_MSU_URL)) {
                    os_strdup(vulnerability->valuestring, (*msu)->url);
                }
            }
        }
    }
}

void wm_vuldet_json_msu_parser_deps(cJSON *json_feed, vu_msu_dep_entry **msu) {
    if (json_feed = cJSON_GetObjectItem(json_feed, "dependencies"), !json_feed) {
        return;
    }

    for (json_feed = json_feed->child; json_tagged_obj(json_feed); json_feed = json_feed->next) {
        if (*msu) {
            os_calloc(1, sizeof(vu_msu_dep_entry), (*msu)->next);
            (*msu)->next->prev = *msu;
            *msu = (*msu)->next;
        } else {
            os_calloc(1, sizeof(vu_msu_dep_entry), (*msu));
        }

        os_strdup(json_feed->string, (*msu)->patch);
        os_calloc(2, sizeof(char *), (*msu)->supers);
        // Duplicate the node key to facilitate the patch correlation
        os_strdup(json_feed->string, (*msu)->supers[0]);
        cJSON *sup_it;
        int i;
        for (i = 1, sup_it = json_feed->child; sup_it && sup_it->valuestring; i++, sup_it = sup_it->next) {
            os_realloc((*msu)->supers, (i + 2) * sizeof(char *), (*msu)->supers);
            os_strdup(sup_it->valuestring, (*msu)->supers[i]);
            (*msu)->supers[i + 1] = NULL;
        }
    }
}

int wm_vuldet_json_msu_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    vu_msu_entries *msu = &parsed_vulnerabilities->msu;

    wm_vuldet_json_msu_parser_vuln(json_feed, &msu->vul);
    wm_vuldet_json_msu_parser_deps(json_feed, &msu->dep);

    return 0;
}

int wm_vuldet_insert_cpe_dic(sqlite3 *db, vu_cpe_dic *w_cpes) {
    int i;
    int entry_id;
    vu_cpe_dic_node *node_it;
    sqlite3_stmt *stmt;

    sqlite3_exec(db, vu_queries[VU_REMOVE_CPE_DIC], NULL, NULL, NULL);

    for (i = 0, entry_id = 0; w_cpes->targets_name[i]; i++) {
        for (node_it = w_cpes->targets[i]; node_it; node_it = wm_vuldet_free_dic_node(node_it), entry_id++) {

            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CPE_HELPER], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }
            sqlite3_bind_int(stmt, 1, entry_id);
            sqlite3_bind_text(stmt, 2, w_cpes->targets_name[i], -1, NULL);
            sqlite3_bind_int(stmt, 3, node_it->action);
            if (wm_vuldet_step(stmt) != SQLITE_DONE) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);

            if (wm_vuldet_insert_cpe_dic_array(db, VU_INSERT_CPE_SOURCE, entry_id, node_it->source)) {
                return OS_INVALID;
            }
            if (wm_vuldet_insert_cpe_dic_array(db, VU_INSERT_CPE_TRANSLATION, entry_id, node_it->translation)) {
                return OS_INVALID;
            }
        }
        free(w_cpes->targets_name[i]);
    }
    os_free(w_cpes->targets_name);
    os_free(w_cpes->targets);

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_METADATA], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, vu_feed_tag[FEED_CPEW], -1, NULL);
    sqlite3_bind_text(stmt, 2, vu_feed_ext[FEED_CPEW], -1, NULL);
    sqlite3_bind_text(stmt, 3, w_cpes->version, -1, NULL);
    sqlite3_bind_text(stmt, 4, w_cpes->format_version, -1, NULL);
    sqlite3_bind_text(stmt, 5, w_cpes->update_date, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    os_free(w_cpes->version);
    os_free(w_cpes->format_version);
    os_free(w_cpes->update_date);

    return 0;
}

int wm_vuldet_insert_cpe_dic_array(sqlite3 *db, vu_query query, int id, vu_cpe_dic_section *section) {
    sqlite3_stmt *stmt;
    int type_value;
    int pattern_it, subpattern_it;
    char ***section_it;
    const char *type;

    for (type_value = 0; type_value < 6; type_value++) {
        switch (type_value) {
            case 0:
                section_it = section->vendor;
                type = vu_cpe_tags[CPE_VENDOR];
            break;
            case 1:
                section_it = section->product;
                type = vu_cpe_tags[CPE_PRODUCT];
            break;
            case 2:
                section_it = section->version;
                type = vu_cpe_tags[CPE_VERSION];
            break;
            case 3:
                section_it = section->sw_edition;
                type = vu_cpe_tags[CPE_SW_EDITION];
            break;
            case 4:
                section_it = section->msu_name;
                type = vu_cpe_tags[CPE_MSU_NAME];
            break;
            case 5:
                section_it = section->target_hw;
                type = vu_cpe_tags[CPE_TARGET_HW];
            break;
        }

        for (pattern_it = 0; section_it && section_it[pattern_it]; pattern_it++) {
            for (subpattern_it = 0; section_it[pattern_it][subpattern_it]; subpattern_it++) {
                char *term = NULL;
                char *cterm;
                char *comp_field = NULL;
                char *condition = NULL;

                if (wm_vuldet_prepare(db, vu_queries[query], -1, &stmt, NULL) != SQLITE_OK) {
                    return wm_vuldet_sql_error(db, stmt);
                }

                cterm = !*section_it[pattern_it][subpattern_it] &&
                                            query == VU_INSERT_CPE_SOURCE &&
                                            !type_value ?
                                            NULL : section_it[pattern_it][subpattern_it];
                w_strdup(cterm, term);

                sqlite3_bind_int(stmt, 1, id);
                sqlite3_bind_int(stmt, 2, pattern_it);
                sqlite3_bind_text(stmt, 3, type, -1, NULL);

                if (query == VU_INSERT_CPE_TRANSLATION) {
                    char *exact_term;
                    if (!wm_vuldet_get_term_condition(term, &exact_term, &comp_field, &condition)) {
                        free(term);
                        term = exact_term;
                        sqlite3_bind_text(stmt, 5, comp_field, -1, NULL);
                        sqlite3_bind_text(stmt, 6, condition, -1, NULL);
                    } else {
                        sqlite3_bind_null(stmt, 5);
                        sqlite3_bind_null(stmt, 6);
                    }
                }

                sqlite3_bind_text(stmt, 4, term, -1, NULL);
                if (wm_vuldet_step(stmt) != SQLITE_DONE) {
                    free(term);
                    free(comp_field);
                    free(condition);
                    return wm_vuldet_sql_error(db, stmt);
                }

                wdb_finalize(stmt);
                free(term);
                free(comp_field);
                free(condition);
            }
        }
    }

    return 0;
}

vu_cpe_dic_node *wm_vuldet_free_dic_node(vu_cpe_dic_node *dic_node) {
    int i;
    vu_cpe_dic_node *prev = dic_node->prev;
    vu_cpe_dic_section *section;

    for (i = 0; i < 2; i++) {
        if (!i) {
            section = dic_node->source;
        } else if (i == 1) {
            section = dic_node->translation;
        }

        w_FreeDoubleArray(section->vendor);
        w_FreeDoubleArray(section->product);
        w_FreeDoubleArray(section->version);
        w_FreeDoubleArray(section->sw_edition);
        w_FreeDoubleArray(section->target_hw);
        w_FreeDoubleArray(section->msu_name);
        free(section->vendor);
        free(section->product);
        free(section->version);
        free(section->sw_edition);
        free(section->target_hw);
        free(section->msu_name);
        free(section);
    }

    free(dic_node);

    return prev;
}

vu_msu_vul_entry *wm_vuldet_free_msu_vul_node(vu_msu_vul_entry *node) {
    vu_msu_vul_entry *prev = node->prev;
    free(node->cveid);
    free(node->product);
    free(node->patch);
    free(node->title);
    free(node->url);
    free(node->subtype);
    free(node->restart_required);
    free(node);
    return prev;
}

vu_msu_dep_entry *wm_vuldet_free_msu_dep_node(vu_msu_dep_entry *node) {
    vu_msu_dep_entry *prev = node->prev;
    free(node->patch);
    w_FreeArray(node->supers);
    free(node->supers);
    free(node);
    return prev;
}

int wm_vuldet_insert_MSU(sqlite3 *db, vu_msu_entries *msu) {
    if (wm_vulndet_insert_msu_vul_entry(db, msu->vul)) {
        return OS_INVALID;
    }

    if (wm_vulndet_insert_msu_dep_entry(db, msu->dep)) {
        return OS_INVALID;
    }

    return 0;
}

int wm_vuldet_clean_agent_cpes(char *agent_id) {
    int retval = OS_INVALID;
    char request[OS_SIZE_128];

    snprintf(request, OS_SIZE_128, vu_queries[VU_SYSC_CLEAN_CPES], agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }
    retval = 0;
end:
    if (retval) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_CLEAN_REQUEST_ERROR, atoi(agent_id));
    }

    return retval;
}

int wm_vuldet_request_hotfixes(sqlite3 *db, char *agent_id) {
    int retval = OS_INVALID;
    char request[OS_MAXSTR];
    cJSON *obj = NULL;
    cJSON *obj_it;
    sqlite3_stmt *stmt = NULL;
    char *last_id;

    if (last_id = wm_vuldet_get_hotfix_scan(agent_id), !last_id) {
        retval = 1;
        goto end;
    }

    snprintf(request, OS_SIZE_128, vu_queries[VU_HOTFIXES_REQUEST], agent_id, last_id);

    if (wm_vuldet_wdb_request(request, OS_MAXSTR)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = 0;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    sqlite3_exec(db, vu_queries[BEGIN_T], NULL, NULL, NULL);
    for (obj_it = obj->child; obj_it; obj_it = obj_it->next) {
        if (!obj_it->child || !obj_it->child->valuestring) {
            goto end;
        }

        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_AGENT_HOTFIXES], -1, &stmt, NULL) != SQLITE_OK) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
            goto end;
        }

        sqlite3_bind_text(stmt, 1, agent_id, -1, NULL);
        sqlite3_bind_text(stmt, 2, obj_it->child->valuestring, -1, NULL);

        if (wm_vuldet_step(stmt) != SQLITE_DONE) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
            goto end;
        }
        wdb_finalize(stmt);
    }

    sqlite3_exec(db, vu_queries[END_T], NULL, NULL, NULL);

    retval = 0;
end:
    wdb_finalize(stmt);
    if (obj) {
        cJSON_Delete(obj);
    }

    os_free(last_id);

    return retval;
}

void wm_vuldet_reset_tables(sqlite3 *db) {
    sqlite3_exec(db, vu_queries[VU_REMOVE_AGENTS_TABLE], NULL, NULL, NULL);
    sqlite3_exec(db, vu_queries[VU_REMOVE_AGENT_CPE], NULL, NULL, NULL);
    sqlite3_exec(db, vu_queries[VU_REMOVE_HOTFIXES_TABLE], NULL, NULL, NULL);
}

int wm_vuldet_get_oswindows_info(agent_software *agent) {
    int retval = OS_INVALID;
    char request[OS_SIZE_128];
    cJSON *obj = NULL;
    cJSON *obj_it;

    snprintf(request, OS_SIZE_128, vu_queries[VU_SYSC_OSWIN_INFO], agent->agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = 0;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    for (obj_it = obj->child; obj_it; obj_it = obj_it->child) {
        if(obj_it->string && strcmp(obj_it->string, "os_release") == 0) {
            w_strdup(obj_it->valuestring, agent->os_release);
            break;
        }
    }

    retval = 0;
end:
    if (obj) {
        cJSON_Delete(obj);
    }
    if (retval) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_CLEAN_REQUEST_ERROR, atoi(agent->agent_id));
    }

    return retval;
}

int wm_vuldet_get_oslinux_info(agent_software *agent) {
    int retval = OS_INVALID;
    char request[OS_SIZE_128];
    cJSON *obj = NULL;
    cJSON *obj_it;

    if (!agent || !agent->agent_id) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_AGENT_NOT_INITIALIZED);
        return OS_INVALID;
    }

    snprintf(request, OS_SIZE_128, vu_queries[VU_SYSC_OSLNX_INFO], agent->agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = 0;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    // We need the OS version in the format 'x.y'
    // To match the NVD vulnerabilities
    if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "os_major")) && obj_it->valuestring) {
        char *version;
        char *major;
        char *minor;
        w_strdup(obj_it->valuestring, major);
        if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "os_minor")) && obj_it->valuestring) {
            w_strdup(obj_it->valuestring, minor);
        } else {
            w_strdup("0", minor);
        }
        size_t ver_length = strlen(major) + strlen(minor) + 2;
        os_calloc(ver_length, sizeof(char), version);
        snprintf(version, ver_length, "%s.%s", major, minor);
        w_strdup(version, agent->os_release);
        os_free(version);
        os_free(major);
        os_free(minor);
    }

    if ((obj_it = obj->child) && (obj_it = cJSON_GetObjectItem(obj_it, "release")) && obj_it->valuestring) {
        w_strdup(obj_it->valuestring, agent->kernel_release);
    }

    retval = 0;
end:
    if (obj) {
        cJSON_Delete(obj);
    }
    if (retval) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_OSINFOLNX_ERROR, atoi(agent->agent_id));
    }

    return retval;
}

int wm_vuldet_index_json(wm_vuldet_db *parsed_vulnerabilities, update_node *update, char *path, char multi_path) {
    int retval = OS_INVALID;
    char *directory;
    DIR *upd_dir = NULL;
    struct dirent *entry;
    char *path_cpy = NULL;

    if (multi_path) {
        char generated_path[PATH_MAX + 1];
        char *pattern;
        regex_t path_pattern;
        regmatch_t match;
        update->update_it = 0;
        os_strdup(path, path_cpy);

        if (*path_cpy != '/') {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MULTIPATH_ERROR, path_cpy, "it is not an absolute path");
            goto end;
        }

        directory = strrchr(path_cpy, '/');

        *directory = '\0';
        pattern = directory + 1;

        if (upd_dir = opendir(path_cpy == directory ? "/" : path_cpy), !upd_dir) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_MULTIPATH_ERROR, path_cpy == directory ? "/" : path_cpy, strerror(errno));
            goto end;
        }

        if (regcomp(&path_pattern, pattern, 0)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_REGEX_COMPILE_ERROR, pattern);
            goto end;
        }

        while (entry = readdir(upd_dir), entry) {
            if ((strcmp(entry->d_name, ".") == 0) ||
                (strcmp(entry->d_name, "..") == 0)) {
                continue;
            }
            snprintf(generated_path, PATH_MAX, "%s/%s", path_cpy, entry->d_name);

            if (w_is_file(generated_path)) {
                if(!regexec(&path_pattern, entry->d_name, 1, &match, 0)) {
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_UPDATE_JSON_FEED, generated_path);
                    if (wm_vuldet_json_parser(generated_path, parsed_vulnerabilities, update) == OS_INVALID) {
                        regfree(&path_pattern);
                        goto end;
                    }
                }
            }
        }
        regfree(&path_pattern);
    } else {
        int result;

        if (result = wm_vuldet_json_parser(path, parsed_vulnerabilities, update), result == OS_INVALID) {
            goto end;
        } else if (result == VU_NOT_NEED_UPDATE) {
            retval = VU_NOT_NEED_UPDATE;
            goto end;
        }
    }

    retval = 0;
end:
    free(path_cpy);

    if (upd_dir) {
        closedir(upd_dir);
    }

    return retval;
}

int wm_vuldet_remove_sequence(sqlite3 *db, char *table) {
    sqlite3_stmt *stmt = NULL;

    if (wm_vuldet_prepare(db, vu_queries[VU_REMOVE_SQUENCE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, table, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_remove_table(sqlite3 *db, char *table) {
    sqlite3_stmt *stmt = NULL;
    char query[OS_SIZE_128 + 1];

    snprintf(query, OS_SIZE_128, vu_queries[DELETE_QUERY], table);
    if (wm_vuldet_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, table, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return wm_vuldet_remove_sequence(db, table);
}

int wm_vuldet_index_nvd(sqlite3 *db, update_node *upd, nvd_vulnerability *nvd_it) {
    time_t index_time = time(NULL);
    int result = 0;
    int cve_count = 0;

    // Clean everything only if there are still residues of an offline update
    // made by multi_path update. For the cases of multi_url or the online
    // update, we just need to clean the NVD for the year being indexed.
    if (result = wm_vuldet_has_offline_update(db), result == OS_INVALID) {
        goto error;
    } else if (result == 1 && upd->multi_path && wm_vuldet_clean_nvd(db)) {
        goto error;
    } else if (!upd->multi_path && wm_vuldet_clean_nvd_year(db, upd->update_it)) {
        goto error;
    }

    while (nvd_it) {
        nvd_vulnerability *r_node = nvd_it;

        if (wm_vuldet_insert_nvd_cve(db, nvd_it, upd->update_it)) {
            goto error;
        }
        nvd_it = nvd_it->next;
        wm_vuldet_free_nvd_node(r_node);
        cve_count++;
    }

    if (wm_vuldet_index_nvd_metadata(db, upd->update_it, cve_count, upd->multi_path || upd->multi_url)) {
        goto error;
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INDEX_TIME, time(NULL) - index_time, "index the NVD");

    return OS_SUCCESS;
error:
    wm_vuldet_free_nvd_list(nvd_it);

    return OS_INVALID;
}

int wm_vuldet_index_redhat(sqlite3 *db, update_node *upd, rh_vulnerability *r_it) {
    time_t index_time = time(NULL);
    sqlite3_stmt *stmt = NULL;
    int result;

    if (upd->multi_path) {
        // Clean everything at this point because all the insertions will be done at once
        if (wm_vuldet_clean_rh(db)) {
            return OS_INVALID;
        }
    }

    while (r_it) {
        char *normalized_name = NULL;

        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CVE], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, r_it->cve_id, -1, NULL);
        sqlite3_bind_text(stmt, 2, r_it->OS, -1, NULL);
        sqlite3_bind_text(stmt, 3, r_it->OS_minor, -1, NULL);

        normalized_name = w_tolower_str(r_it->package_name);
        sqlite3_bind_text(stmt, 4, normalized_name, -1, NULL);

        sqlite3_bind_text(stmt, 5, vu_package_comp[VU_COMP_L], -1, NULL);

        // If the epoch is 0, we don't save it because it is not necessary
        if (!strcmp(r_it->package_version, version_null) || strncmp("0:", r_it->package_version, 2)) {
            sqlite3_bind_text(stmt, 6, r_it->package_version, -1, NULL);
        } else {
            sqlite3_bind_text(stmt, 6, (r_it->package_version + 2), -1, NULL);
        }

        sqlite3_bind_int(stmt, 7, 0);
        sqlite3_bind_int(stmt, 8, 0);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            os_free(normalized_name);
            return wm_vuldet_sql_error(db, stmt);
        }

        wdb_finalize(stmt);
        os_free(normalized_name);

        rh_vulnerability *rvul_aux = r_it;
        r_it = r_it->prev;
        free(rvul_aux->cve_id);
        free(rvul_aux->package_name);
        free(rvul_aux->package_version);
        free(rvul_aux->OS_minor);
        free(rvul_aux);
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_INDEX_TIME, time(NULL) - index_time, "index the Red Hat feed");

    return 0;
}

int wm_vuldet_clean_rh(sqlite3 *db) {
    char open_db = 0;

    if (!db) {
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, NULL);
        }

        open_db = 1;
    }

    if (wm_vuldet_remove_target_table(db, CVE_TABLE, vu_feed_tag[FEED_RHEL5])        ||
        wm_vuldet_remove_target_table(db, CVE_TABLE, vu_feed_tag[FEED_RHEL6])        ||
        wm_vuldet_remove_target_table(db, CVE_TABLE, vu_feed_tag[FEED_RHEL7])        ||
        wm_vuldet_remove_target_table(db, CVE_TABLE, vu_feed_tag[FEED_RHEL8])        ||
        wm_vuldet_remove_target_table(db, METADATA_TABLE, vu_feed_tag[FEED_REDHAT])  ||
        wm_vuldet_remove_target_table(db, CVE_INFO_TABLE, vu_feed_tag[FEED_REDHAT])) {
        return OS_INVALID;
    }
    if (open_db) {
        sqlite3_close_v2(db);
    }
    return 0;
}

int wm_vuldet_clean_wcpe(sqlite3 *db) {
    char open_db = 0;

    if (!db) {
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, NULL);
        }

        open_db = 1;
    }

    if (wm_vuldet_remove_target_table(db, METADATA_TABLE, vu_feed_tag[FEED_CPEW])        ||
        wm_vuldet_remove_table(db, CPEH_SOURCE_TABLE) ||
        wm_vuldet_remove_table(db, CPET_TRANSL_TABLE) ||
        wm_vuldet_remove_table(db, CPE_HELPER_TABLE)) {
        return OS_INVALID;
    }
    if (open_db) {
        sqlite3_close_v2(db);
    }
    return 0;
}

char *wm_vuldet_cpe_str(cpe *cpe_s) {
    char *generated_cpe;

    os_calloc(OS_SIZE_256 + 1, sizeof(char), generated_cpe);

    snprintf(generated_cpe, OS_SIZE_256, "%c:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s",
        cpe_s->part ? *cpe_s->part : ' ',
        cpe_s->vendor,
        cpe_s->product,
        cpe_s->version ? cpe_s->version : "",
        cpe_s->update ? cpe_s->update : "",
        cpe_s->edition ? cpe_s->edition : "",
        cpe_s->language ? cpe_s->language : "",
        cpe_s->sw_edition ? cpe_s->sw_edition : "",
        cpe_s->target_sw ? cpe_s->target_sw : "",
        cpe_s->target_hw ? cpe_s->target_hw : "",
        cpe_s->other ? cpe_s->other : "");

    return generated_cpe;
}

cJSON *wm_vuldet_json_fread(char *json_file) {
    cJSON *feed;
    char compress;

    if (wstr_end(json_file, ".gz")) {
        compress = 1;

        if (w_uncompress_gzfile(json_file, VU_TEMP_FILE)) {
            return NULL;
        }
    } else {
        compress = 0;
    }

    feed = json_fread(compress ? VU_TEMP_FILE : json_file, 0);

    if (compress && remove(VU_TEMP_FILE) < 0) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_FILE, strerror(errno));
    }

    return feed;
}

cJSON *wm_vuldet_get_cvss(const char *scoring_vector) {
    cJSON *cvss_json = NULL;
    char impact;
    const char *vector_it;
    STATIC char *cvss_attack_vector = "AV:";
    STATIC char *cvss_access_complexity = "AC:";
    STATIC char *cvss_authentication = "Au:"; // CVSS2 only
    STATIC char *cvss_confidentiality_impact = "C:";
    STATIC char *cvss_integrity_impact = "I:";
    STATIC char *cvss_availability = "A:";
    STATIC char *cvss_privileges_required = "PR:"; // CVSS3 only
    STATIC char *cvss_user_interaction = "UI:"; // CVSS3 only
    STATIC char *cvss_scope = "S:"; // CVSS3 only

    if (NULL == scoring_vector)
        return NULL;

    // Making a copy to avoid modifying the original string
    char* vector = NULL;
    os_strdup(scoring_vector, vector);

    if (cvss_json = cJSON_CreateObject(), cvss_json) {
        char* token = NULL;

        for (token = strtok(vector, "/"); NULL != token; token = strtok(NULL, "/"))
        {
            // Attack vector
            if (vector_it = strstr(token, cvss_attack_vector), vector_it) {
                impact = *(vector_it + strlen(cvss_attack_vector));
                if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "local");
                } else if (impact == 'A') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "adjacent_network");
                } else if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "network");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "attack_vector", "physical");
                }
            }
            // Access complexity
            else if (vector_it = strstr(token, cvss_access_complexity), vector_it) {
                impact = *(vector_it + strlen(cvss_access_complexity));
                if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "access_complexity", "low");
                } else if (impact == 'M') {
                    cJSON_AddStringToObject(cvss_json, "access_complexity", "medium");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "access_complexity", "high");
                }
            }
            // Authentication
            else if (vector_it = strstr(token, cvss_authentication), vector_it) {
                impact = *(vector_it + strlen(cvss_authentication));
                if (impact == 'M') {
                    cJSON_AddStringToObject(cvss_json, "authentication", "multiple");
                } else if (impact == 'S') {
                    cJSON_AddStringToObject(cvss_json, "authentication", "single");
                } else if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "authentication", "none");
                }
            }
            // Confidentiality impact
            else if (vector_it = strstr(token, cvss_confidentiality_impact), vector_it) {
                impact = *(vector_it + strlen(cvss_confidentiality_impact));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "confidentiality_impact", "high");
                }
            }
            // User interaction
            else if (vector_it = strstr(token, cvss_user_interaction), vector_it) {
                impact = *(vector_it + strlen(cvss_user_interaction));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "user_interaction", "none");
                } else if (impact == 'R') {
                    cJSON_AddStringToObject(cvss_json, "user_interaction", "required");
                }
            }
            // Integrity impact
            else if (vector_it = strstr(token, cvss_integrity_impact), vector_it) {
                impact = *(vector_it + strlen(cvss_integrity_impact));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "integrity_impact", "high");
                }
            }
            // Availability
            else if (vector_it = strstr(token, cvss_availability), vector_it) {
                impact = *(vector_it + strlen(cvss_availability));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "availability", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "availability", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "availability", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "availability", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "availability", "high");
                }
            }
            // Privileges required
            else if (vector_it = strstr(token, cvss_privileges_required), vector_it) {
                impact = *(vector_it + strlen(cvss_privileges_required));
                if (impact == 'N') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "none");
                } else if (impact == 'P') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "partial");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "complete");
                } else if (impact == 'L') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "low");
                } else if (impact == 'H') {
                    cJSON_AddStringToObject(cvss_json, "privileges_required", "high");
                }
            }
            // Scope
            else if (vector_it = strstr(token, cvss_scope), vector_it) {
                impact = *(vector_it + strlen(cvss_scope));
                if (impact == 'U') {
                    cJSON_AddStringToObject(cvss_json, "scope", "unchanged");
                } else if (impact == 'C') {
                    cJSON_AddStringToObject(cvss_json, "scope", "changed");
                }
            }
        }
    }

    os_free(vector);

    return cvss_json;
}

int wm_vuldet_get_dist_ref(const char *dist_name, const char *dist_ver, int *dist_ref, int *dist_ver_ref) {
    if (strcasestr(dist_name, vu_feed_tag[FEED_REDHAT]) || strcasestr(dist_name, "red hat")) {
        switch (*dist_ver) {
            case '5':
                *dist_ver_ref = FEED_RHEL5;
                break;
            case '6':
                *dist_ver_ref = FEED_RHEL6;
                break;
            case '7':
                *dist_ver_ref = FEED_RHEL7;
                break;
            case '8':
                *dist_ver_ref = FEED_RHEL8;
                break;
            default:
                return OS_INVALID;
        }
        *dist_ref = FEED_REDHAT;
        return 0;
    }

    return OS_INVALID;;
}

int wm_vuldet_get_term_condition(char *i_term, char **term, char **comp_field, char **condition) {
    STATIC regex_t *regex = NULL;
    regmatch_t matches[5];

    if (!i_term) {
        return 1;
    }

    if (!regex) {
        const char *pattern = "^([^ ]+)[ ]+\\([ ]*([^ ]+)[ ]+([^ ]+)[ ]+([^ ]+)\\)";
        os_calloc(1, sizeof(regex_t), regex);

        if (regcomp(regex, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    int retval = 1;
    if (!regexec(regex, i_term, 5, matches, 0)) {
        int i;
        char **target;
        char *part = NULL;
        char *op = NULL;
        char *check = NULL;
        char *op_value = NULL;
        size_t size;

        for (i = 1; i < 5; i++) {
            int start = (&matches[i])->rm_so;
            int end = (&matches[i])->rm_eo;

            switch (i) {
                case 1:
                    target = &part;
                break;
                case 2:
                    target = &op;
                break;
                case 3:
                    target = &check;
                break;
                case 4:
                    target = &op_value;
                break;
                default:
                    goto free_mem;
            }

            if (!(start >= 0 && end >= 0 && end > start && (unsigned int) (end - start) <= strlen(i_term))) {
                goto free_mem;
            }

            os_strdup(i_term + start, *target);
            (*target)[end - start] = '\0';
        }

        // Unnecessary check to remove a false positive reported by Coverity
        if (!part || !op || !check || !op_value) {
            goto free_mem;
        }

        size = strlen(check) + strlen(op_value) + 3;
        os_calloc(size, sizeof(char), *condition);
        snprintf(*condition, size, "%s %s", check, op_value);
        os_strdup(part, *term);
        os_strdup(op, *comp_field);

        retval = 0;
free_mem:
        os_free(part);
        os_free(op);
        os_free(check);
        os_free(op_value);
    }

    if (regex) {
        regfree(regex);
        os_free(regex);
    }

    return retval;
}

cpe *wm_vuldet_generate_cpe(const char *part, const char *vendor, const char *product, const char *version, const char *update, const char *edition, const char *language, const char *sw_edition, const char *target_sw, const char *target_hw, const char *other, const int id, const char *msu_name) {
    cpe *new_cpe;
    os_calloc(1, sizeof(cpe), new_cpe);

    // CPE fields
    w_strdup(part, new_cpe->part);
    w_strdup(vendor, new_cpe->vendor);
    w_strdup(product, new_cpe->product);
    w_strdup(version, new_cpe->version);
    w_strdup(update, new_cpe->update);
    w_strdup(edition, new_cpe->edition);
    w_strdup(language, new_cpe->language);
    w_strdup(sw_edition, new_cpe->sw_edition);
    w_strdup(target_sw, new_cpe->target_sw);
    w_strdup(target_hw, new_cpe->target_hw);
    w_strdup(other, new_cpe->other);

    // Extra fields
    new_cpe->id = id;
    w_strdup(msu_name, new_cpe->msu_name);
    new_cpe->check_hotfix = new_cpe->msu_name ? 1 : 0;
    new_cpe->raw = wm_vuldet_cpe_str(new_cpe);

    return new_cpe;
}

void wm_vuldet_run_scan(wm_vuldet_t *vuldet) {

    mtinfo(WM_VULNDETECTOR_LOGTAG, VU_START_SCAN);

    if (wm_vuldet_nvd_empty() == 0) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_NVD_EMPTY);
        vuldet->last_detection = time(NULL);
        return;
    }

    if (wm_vuldet_set_agents_info(&vuldet->agents_software, vuldet->updates)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_NO_AGENT_ERROR);
    } else {
        if (wm_vuldet_check_agent_vulnerabilities(vuldet->agents_software, &vuldet->flags, vuldet->ignore_time)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_AG_CHECK_ERR);
        } else {
            mtinfo(WM_VULNDETECTOR_LOGTAG, VU_END_SCAN);
        }
    }

    agent_software *agent;
    for (agent = vuldet->agents_software; agent;) {
        agent_software *agent_aux = agent->next;

        wm_vuldet_free_agent_software(agent);
        if (agent_aux) {
            agent = agent_aux;
        } else {
            break;
        }
    }

    vuldet->agents_software = NULL;
    vuldet->last_detection = time(NULL);

}

void wm_vuldet_run_sleep(wm_vuldet_t * vuldet) {
    time_t t_now = time(NULL);
    time_t time_sleep = (vuldet->last_detection + vuldet->detection_interval) - t_now;
    int i;

    if (time_sleep <= 0) {
        time_sleep = 1;
        i = OS_SUPP_SIZE;
    } else {
        i = 0;
    }

    // Check the remaining time for all updates and adjust the sleep time
    for (; i < OS_SUPP_SIZE; i++) {
        if (vuldet->updates[i]) {
            time_t t_diff = (vuldet->updates[i]->last_update + vuldet->updates[i]->interval) - t_now;
            // Stop checking if we have any pending updates
            if (t_diff <= 0) {
                time_sleep = 1;
                break;
            } else if (t_diff < time_sleep) {
                time_sleep = t_diff;
            }
        }
    }

    mtdebug2(WM_VULNDETECTOR_LOGTAG, "Sleeping for %lu seconds...", time_sleep);
    sleep(time_sleep);
}

void wm_vuldet_init(wm_vuldet_t * vuldet) {
    wm_vuldet_flags *flags = &vuldet->flags;
    update_node **updates = vuldet->updates;
    int i;

    if (!flags->enabled) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, "Module disabled. Exiting...");
        pthread_exit(NULL);
    }

    for (i = 0; vuldet->queue_fd = StartMQ(DEFAULTQPATH, WRITE), vuldet->queue_fd < 0 && i < WM_MAX_ATTEMPTS; i++) {
        sleep(WM_MAX_WAIT);
    }

    if (i == WM_MAX_ATTEMPTS) {
        mterror(WM_VULNDETECTOR_LOGTAG, "Can't connect to queue.");
        pthread_exit(NULL);
    }

    vu_queue = &vuldet->queue_fd;

    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();


    for (i = 0; SSL_library_init() < 0 && i < WM_MAX_ATTEMPTS; i++) {
        sleep(WM_MAX_WAIT);
    }

    if (i == WM_MAX_ATTEMPTS) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SSL_LIBRARY_ERROR);
        pthread_exit(NULL);
    }

    if (!flags->run_on_start) {
        vuldet->last_detection = time(NULL);
        for (i = 0; i < OS_SUPP_SIZE; i++) {
            if (updates[i]) {
                updates[i]->last_update = time(NULL);
            }
        }
    }
}

int wm_vuldet_select_scan_type(char *agent_id, time_t ignore_time, char *hotfix_config_enabled) {
    int retval = OS_INVALID;
    char request[OS_SIZE_6144];
    cJSON *obj = NULL;
    cJSON *obj_it;

    snprintf(request, OS_SIZE_128, vu_queries[VU_LAST_SCAN_REQUEST], agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_6144)) {
        goto end;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        goto end;
    }

    if (wm_vuldet_wdb_empty_answ(request)) {
       retval = VU_SOFTWARE_FULL_REQ;
       goto end;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj) {
        goto end;
    }

    if ((obj_it = obj->child) &&
        (obj_it = cJSON_GetObjectItem(obj_it, "hotfix_scan_id")) &&
        obj_it->valuestring && strcmp(obj_it->valuestring, "0")) {
        *hotfix_config_enabled = 1;
    }

    if (!(obj_it = obj->child) ||
        !(obj_it = cJSON_GetObjectItem(obj_it, "last_scan")) ||
        !obj_it->valueint) {
        retval = VU_SOFTWARE_FULL_REQ;
        goto end;
    }

    // Check if the agent needs to be completely scanned
    time_t last_scan = obj_it->valueint;
    if ((last_scan + ignore_time) < time(NULL)) {
        retval = VU_SOFTWARE_FULL_REQ;
    } else {
        retval = VU_SOFTWARE_REQUEST;
    }

end:
    if (obj) {
        cJSON_Delete(obj);
    }

    return retval;
}

void wm_vuldet_update_last_scan(char *agent_id) {
    char query[OS_SIZE_256];

    snprintf(query, OS_SIZE_256, vu_queries[VU_UPDATE_SCAN_MET], agent_id, time(NULL), vu_get_version());
    if (wm_vuldet_wdb_request(query, OS_SIZE_256)) {
        mtwarn(WM_VULNDETECTOR_LOGTAG, VU_WDB_LASTSCAN_ERROR, atoi(agent_id));
    }
}

int wm_vuldet_get_wdb_socket() {
    if (wdb_sock < 0) {
        int i;
        for (i = 0; i < VU_MAX_WAZUH_DB_ATTEMPS && (wdb_sock = OS_ConnectUnixDomain(WDB_LOCAL_SOCK_PATH, SOCK_STREAM, OS_SIZE_6144)) < 0; i++) {
            mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_SOCKET_RETRY, WDB_LOCAL_SOCK_PATH, i);
            sleep(i * i);
            if (i == VU_MAX_WAZUH_DB_ATTEMPS) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_SOCKET_RETRY_ERROR, WDB_LOCAL_SOCK_PATH);
                i = -1;
            }
        }
    }

    return wdb_sock;
}

int wm_vuldet_send_wdb(char *msg) {
    int res = OS_INVALID;
    int sock = wm_vuldet_get_wdb_socket();
    size_t size = strlen(msg) + 1;

    if (sock < 0 || (res = OS_SendSecureTCP(sock, size, msg))) {
        wm_vuldet_close_wdb();
    }
    return res;
}

int wm_vuldet_recv_wdb(char *msg, size_t size) {
    int res;
    int sock = wm_vuldet_get_wdb_socket();
    if (sock < 0 || (res = OS_RecvSecureTCP(sock, msg, size)) < 3) {
        wm_vuldet_close_wdb();
        return OS_INVALID;
    }
    msg[res] = '\0';
    return res;
}

void wm_vuldet_close_wdb() {
    if (wdb_sock >= 0) {
        close(wdb_sock);
        wdb_sock = -1;
    }
}

char *wm_vuldet_get_hotfix_scan(char *agent_id) {
    char request[OS_SIZE_128];
    char *scan_id = NULL;
    cJSON *obj = NULL;

    // Get the last scan ID
    snprintf(request, OS_SIZE_128, vu_queries[VU_HOTF_SCAN_REQUEST], agent_id);

    if (wm_vuldet_wdb_request(request, OS_SIZE_128)) {
        return NULL;
    }

    if (!wm_vuldet_wdb_valid_answ(request)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, request);
        return NULL;
    }

    // Check empty answers
    if (wm_vuldet_wdb_empty_answ(request)) {
        return NULL;
    }

    request[0] = request[1] = ' ';

    if (obj = cJSON_Parse(request), !obj || !obj->child || !obj->child->child) {
        goto end;
    }

    os_malloc(OS_SIZE_128, scan_id);
    snprintf(scan_id, OS_SIZE_128, "%i", obj->child->child->valueint);

end:
    if (obj) {
        cJSON_Delete(obj);
    }

    return scan_id;
}

int wm_vuldet_get_last_software_scan(char *agent_id, char scan_id[OS_SIZE_128]) {
    char buffer[OS_SIZE_6144];
    char json_str[OS_SIZE_6144 + 10];
    int retval = OS_INVALID;
    cJSON *obj = NULL;
    const char *jsonErrPtr;

    *scan_id = '\0';

    // Request the ID of the last scan
    snprintf(buffer, OS_SIZE_6144, vu_queries[VU_SOFT_SCAN_REQUEST], agent_id);
    if (wm_vuldet_wdb_request(buffer, OS_SIZE_6144)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SYSC_SCAN_REQUEST_ERROR, atoi(agent_id));
        goto end;
    }

    if (wm_vuldet_wdb_valid_answ(buffer)) {
        // Check empty answers
        if (wm_vuldet_wdb_empty_answ(buffer)) {
            retval = 1;
            goto end;
        }
        buffer[0] = buffer[1] = ' ';
        snprintf(json_str, OS_SIZE_6144 + 10, "{\"data\":%s}", buffer);
    } else {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, buffer);
        goto end;
    }

    cJSON *data;
    obj = cJSON_ParseWithOpts(json_str, &jsonErrPtr, 0);
    if (!obj || !cJSON_IsObject(obj) ||
        !(data = cJSON_GetObjectItem(obj, "data")) ||
        !data->child || !data->child->child) {
        goto end;
    }

    snprintf(scan_id, OS_SIZE_128, "%i", (int) cJSON_GetObjectItem(obj, "data")->child->child->valuedouble);

    retval = 0;
end:
    if (obj) {
        cJSON_Delete(obj);
    }

    return retval;
}

char *wm_vuldet_normalize_date(char **date) {
    STATIC regex_t *r_timestap = NULL;
    STATIC regex_t *r_utc = NULL;
    STATIC regex_t *r_only_date = NULL;
    int success = 0;
    char matched_pattern = 0;

    if (!*date) {
        goto end;
    }

    if (!r_timestap) {
        const char *pattern = "([0-9]{4}[-/][0-9]{2}[-/][0-9]{2}[ T][0-9]{2}:[0-9]{2}:[0-9]{2}[A-Z])";
        os_calloc(1, sizeof(regex_t), r_timestap);
        if (regcomp(r_timestap, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    if (!r_utc) {
        const char *pattern = "([0-9]{4}[-/][0-9]{2}[-/][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) UTC";
        os_calloc(1, sizeof(regex_t), r_utc);
        if (regcomp(r_utc, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    if (!r_only_date) {
        const char *pattern = "([0-9]{4}[-/][0-9]{2}[-/][0-9]{2})";
        os_calloc(1, sizeof(regex_t), r_only_date);
        if (regcomp(r_only_date, pattern, REG_EXTENDED)) {
            mterror_exit(WM_VULNDETECTOR_LOGTAG, "Unexpected error when compiling '%s'.", pattern);
        }
    }

    regmatch_t matched_date;
    memset(&matched_date, 0, sizeof(regmatch_t));

    if (!regexec(r_timestap, *date, 1, &matched_date, 0)) {
        matched_pattern = 1;
    } else if (!regexec(r_utc, *date, 1, &matched_date, 0)) {
        matched_pattern = 2;
    } else if (!regexec(r_only_date, *date, 1, &matched_date, 0)) {
        matched_pattern = 3;
    }

    if (matched_pattern) {
            int start = matched_date.rm_so;
            int end = matched_date.rm_eo;
            size_t size = end - start;
            char *new_date = NULL;
            char *found;

            if (!(start >= 0 && end >= 0 && end > start && (unsigned int) (size) <= strlen(*date))) {
                goto end;
            }

            (*date)[start + end] = '\0';
            new_date = wstr_replace((char *) *date + start, "/", "-") ;
            os_free(*date);

            switch (matched_pattern) {
                case 1:
                    if (found = strchr(new_date, ' '), found) {
                        *found = 'T';
                    }
                break;
                case 2:
                    if (found = strchr(new_date, ' '), !found) {
                        free(new_date);
                        goto end;
                    }
                    *found = 'T';
                    if (found = strchr(new_date, ' '), !found) {
                        free(new_date);
                        goto end;
                    }
                    *(found++) = 'Z';
                    *found = '\0';
                break;
                case 3:
                    // The date is valid
                break;
                default:
                    free(new_date);
                    goto end;
            }

            *date = new_date;
            success = 1;
    }

end:
    if (!success) {
        // Check if it is a epoch format
        if (!*date || !w_str_is_number(*date)) {
           os_free(*date);
        }
    }

    if (r_timestap) {
        regfree(r_timestap);
        os_free(r_timestap);
    }
    if (r_utc) {
        regfree(r_utc);
        os_free(r_utc);
    }
    if (r_only_date) {
        regfree(r_only_date);
        os_free(r_only_date);
    }

    return *date;
}

int wm_vulndet_insert_msu_vul_entry(sqlite3 *db, vu_msu_vul_entry *vul) {
    sqlite3_stmt *stmt = NULL;
    int result;

    sqlite3_exec(db, vu_queries[VU_REMOVE_MSU], NULL, NULL, NULL);

    for (; vul; vul = wm_vuldet_free_msu_vul_node(vul)) {
        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_MSU], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        sqlite3_bind_text(stmt, 1, vul->cveid, -1, NULL);
        sqlite3_bind_text(stmt, 2, vul->product, -1, NULL);
        sqlite3_bind_text(stmt, 3, vul->patch, -1, NULL);
        sqlite3_bind_text(stmt, 4, vul->title, -1, NULL);
        sqlite3_bind_text(stmt, 5, vul->url, -1, NULL);
        sqlite3_bind_text(stmt, 6, vul->subtype, -1, NULL);
        sqlite3_bind_text(stmt, 7, vul->restart_required, -1, NULL);
        sqlite3_bind_int(stmt, 8, vul->check_type);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }
        wdb_finalize(stmt);
    }

    return 0;
}

int wm_vulndet_insert_msu_dep_entry(sqlite3 *db, vu_msu_dep_entry *dep) {
    sqlite3_stmt *stmt = NULL;
    int result;

    sqlite3_exec(db, vu_queries[VU_REMOVE_MSU_SUP], NULL, NULL, NULL);

    for (; dep; dep = wm_vuldet_free_msu_dep_node(dep)) {
        int i;
        for (i = 0; dep->supers[i]; i++) {
            if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_MSU_SUPER], -1, &stmt, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt);
            }

            sqlite3_bind_text(stmt, 1, dep->patch, -1, NULL);
            sqlite3_bind_text(stmt, 2, dep->supers[i], -1, NULL);

            if (result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
                return wm_vuldet_sql_error(db, stmt);
            }
            wdb_finalize(stmt);
        }
    }

    return 0;
}

void wm_vuln_check_msu_type(vu_msu_vul_entry *msu, cJSON *patchs) {
    const char *win10_pattern = "Windows 10";
    const char *version_pattern = " Version ";

    if (strncmp(msu->product, win10_pattern, strlen(win10_pattern))) {
        msu->check_type = 1;
        return;
    }

    if (!strncmp(msu->product + strlen(win10_pattern), version_pattern, strlen(version_pattern))) {
        msu->check_type = strtol(msu->product + strlen(win10_pattern) + strlen(version_pattern), NULL, 10);
        return;
    }

    for (; patchs; patchs = patchs->next) {
        cJSON *product_json = cJSON_GetObjectItem(patchs, "product");
        char *product_name;
        if (!product_json || !(product_name = product_json->valuestring)) {
            continue;
        }

        if (strncmp(product_name, win10_pattern, strlen(win10_pattern)) ||
            strncmp(product_name + strlen(win10_pattern), version_pattern, strlen(version_pattern))) {
            continue;
        }
        msu->check_type = 1;
        break;
    }
}

#endif
